mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-11-03 15:52:30 +01:00 
			
		
		
		
	Compare commits
	
		
			412 Commits
		
	
	
		
			manager-v4
			...
			manager-v5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					de2285d5e9 | ||
| 
						 | 
					b2483ba437 | ||
| 
						 | 
					a82a5e5a49 | ||
| 
						 | 
					d161a02e71 | ||
| 
						 | 
					d2b6a700b1 | ||
| 
						 | 
					af203cef24 | ||
| 
						 | 
					673e917e76 | ||
| 
						 | 
					a3bd41db54 | ||
| 
						 | 
					0d9527921a | ||
| 
						 | 
					f0e4aec0af | ||
| 
						 | 
					b0d65b5edd | ||
| 
						 | 
					75532ef591 | ||
| 
						 | 
					9a6d1bd700 | ||
| 
						 | 
					a7ed6c15d3 | ||
| 
						 | 
					5ee49ba065 | ||
| 
						 | 
					d34bd47bea | ||
| 
						 | 
					f17792380b | ||
| 
						 | 
					c11920110e | ||
| 
						 | 
					ec5a993fea | ||
| 
						 | 
					d250c2cc89 | ||
| 
						 | 
					767e73f40c | ||
| 
						 | 
					3f699c9d2f | ||
| 
						 | 
					50dbd9befd | ||
| 
						 | 
					760e01bf92 | ||
| 
						 | 
					543f435b1e | ||
| 
						 | 
					91337218b3 | ||
| 
						 | 
					afff3c0a49 | ||
| 
						 | 
					a1871e4bc3 | ||
| 
						 | 
					3aa0294cd4 | ||
| 
						 | 
					310b266251 | ||
| 
						 | 
					21b1b5098e | ||
| 
						 | 
					a3a4a5d8a5 | ||
| 
						 | 
					270536f33c | ||
| 
						 | 
					66bb433cc6 | ||
| 
						 | 
					bd4ef1a03a | ||
| 
						 | 
					aa2d9a3bf1 | ||
| 
						 | 
					fd6cbb138c | ||
| 
						 | 
					aa75c8e5e4 | ||
| 
						 | 
					c461fc6daa | ||
| 
						 | 
					96eaa833f5 | ||
| 
						 | 
					863b13a694 | ||
| 
						 | 
					e6fea4e6dd | ||
| 
						 | 
					83bfc13056 | ||
| 
						 | 
					bc4f09209b | ||
| 
						 | 
					967ca17238 | ||
| 
						 | 
					595c72147c | ||
| 
						 | 
					f3c3b5a649 | ||
| 
						 | 
					1cd2c5e653 | ||
| 
						 | 
					b2873dd44b | ||
| 
						 | 
					bb80ab4026 | ||
| 
						 | 
					80cabb338b | ||
| 
						 | 
					2c69e2c151 | ||
| 
						 | 
					c1dd23f5e0 | ||
| 
						 | 
					f93624a41c | ||
| 
						 | 
					9f4559a059 | ||
| 
						 | 
					fd05cad303 | ||
| 
						 | 
					d58b06e493 | ||
| 
						 | 
					2f0b549027 | ||
| 
						 | 
					87dbd7e541 | ||
| 
						 | 
					96e5da36be | ||
| 
						 | 
					43745edac0 | ||
| 
						 | 
					f5ceee547c | ||
| 
						 | 
					b612bce779 | ||
| 
						 | 
					2e88e5e9c7 | ||
| 
						 | 
					9a7aa25c90 | ||
| 
						 | 
					c4420fe932 | ||
| 
						 | 
					a5260f3a95 | ||
| 
						 | 
					47ccf4b1f5 | ||
| 
						 | 
					a356b21895 | ||
| 
						 | 
					614a36c888 | ||
| 
						 | 
					f520fe36bd | ||
| 
						 | 
					7273a1c34d | ||
| 
						 | 
					dc45cbce37 | ||
| 
						 | 
					708d8f75c0 | ||
| 
						 | 
					bd37d90228 | ||
| 
						 | 
					b1ad691464 | ||
| 
						 | 
					f4e7baf31e | ||
| 
						 | 
					c0e60c41f2 | ||
| 
						 | 
					c8dad43e00 | ||
| 
						 | 
					a8f124704d | ||
| 
						 | 
					eed2816491 | ||
| 
						 | 
					a6334b3e35 | ||
| 
						 | 
					334beebfeb | ||
| 
						 | 
					13dad848bd | ||
| 
						 | 
					e518f4cef8 | ||
| 
						 | 
					c8fd5da2da | ||
| 
						 | 
					3a74729ecc | ||
| 
						 | 
					49c672ac4d | ||
| 
						 | 
					b570cb5b77 | ||
| 
						 | 
					97bf388471 | ||
| 
						 | 
					1a32aaea6f | ||
| 
						 | 
					4635883dec | ||
| 
						 | 
					3ba6db4a50 | ||
| 
						 | 
					2f1de25747 | ||
| 
						 | 
					f60fd42ac0 | ||
| 
						 | 
					ecc8f9c792 | ||
| 
						 | 
					e295dfdcf7 | ||
| 
						 | 
					fc42c25390 | ||
| 
						 | 
					27d5858e06 | ||
| 
						 | 
					e1ef732b60 | ||
| 
						 | 
					9840b95c21 | ||
| 
						 | 
					a6f8446d81 | ||
| 
						 | 
					c1c844c830 | ||
| 
						 | 
					389299afd1 | ||
| 
						 | 
					826543a291 | ||
| 
						 | 
					4ac83cfded | ||
| 
						 | 
					64c363ce53 | ||
| 
						 | 
					cca4347bf9 | ||
| 
						 | 
					3ae3d4926a | ||
| 
						 | 
					36025d6d9f | ||
| 
						 | 
					e171362e3e | ||
| 
						 | 
					3e0bf2ae15 | ||
| 
						 | 
					07aa9f4b8b | ||
| 
						 | 
					b2d9f3fc64 | ||
| 
						 | 
					5fb3e9167e | ||
| 
						 | 
					99c74b31be | ||
| 
						 | 
					ce5b13824e | ||
| 
						 | 
					c39170c42e | ||
| 
						 | 
					fd19fbf300 | ||
| 
						 | 
					166469827f | ||
| 
						 | 
					a34ed538b6 | ||
| 
						 | 
					5f22d3e055 | ||
| 
						 | 
					fdd700f3e5 | ||
| 
						 | 
					adf930f126 | ||
| 
						 | 
					05f41928cd | ||
| 
						 | 
					2ee0829871 | ||
| 
						 | 
					743560825d | ||
| 
						 | 
					e3d84ac349 | ||
| 
						 | 
					266c832b30 | ||
| 
						 | 
					f5374a024e | ||
| 
						 | 
					4956d826fb | ||
| 
						 | 
					f5cc2af5d0 | ||
| 
						 | 
					5880d4a6ec | ||
| 
						 | 
					ae05dce958 | ||
| 
						 | 
					9ebe372a9a | ||
| 
						 | 
					e6e04cc5b3 | ||
| 
						 | 
					12352510fd | ||
| 
						 | 
					2b3d927937 | ||
| 
						 | 
					a8890740f5 | ||
| 
						 | 
					f60d7ee54b | ||
| 
						 | 
					896ca2ef6b | ||
| 
						 | 
					c036f6d529 | ||
| 
						 | 
					6f457c0c59 | ||
| 
						 | 
					13bf1b27b4 | ||
| 
						 | 
					f742bb1c47 | ||
| 
						 | 
					aa0b9e2db2 | ||
| 
						 | 
					c10076f7ed | ||
| 
						 | 
					bcd92499f2 | ||
| 
						 | 
					b2bb0d4f72 | ||
| 
						 | 
					e140481f14 | ||
| 
						 | 
					186bd11463 | ||
| 
						 | 
					a0490d6687 | ||
| 
						 | 
					beef740ade | ||
| 
						 | 
					2ac7786a90 | ||
| 
						 | 
					a3fb5e910f | ||
| 
						 | 
					319afe86b5 | ||
| 
						 | 
					762ab66b86 | ||
| 
						 | 
					0c239a42de | ||
| 
						 | 
					e9322fba26 | ||
| 
						 | 
					39b6df27b3 | ||
| 
						 | 
					b1ee284e7f | ||
| 
						 | 
					e986332bf2 | ||
| 
						 | 
					48f9b27381 | ||
| 
						 | 
					42a6e0dd10 | ||
| 
						 | 
					d4798b02ac | ||
| 
						 | 
					963edfe8ab | ||
| 
						 | 
					53237f3ae0 | ||
| 
						 | 
					64da9281a4 | ||
| 
						 | 
					ab7fd9799d | ||
| 
						 | 
					f6bcc84251 | ||
| 
						 | 
					35dc3d9df9 | ||
| 
						 | 
					566714a75d | ||
| 
						 | 
					c92f30b122 | ||
| 
						 | 
					294ad094c4 | ||
| 
						 | 
					c1a0f520f9 | ||
| 
						 | 
					773c24b7fc | ||
| 
						 | 
					8f926c7ca9 | ||
| 
						 | 
					c562cbc2bb | ||
| 
						 | 
					3fbbb0865a | ||
| 
						 | 
					7d5f612a48 | ||
| 
						 | 
					4a5a36440b | ||
| 
						 | 
					43dd5cfea1 | ||
| 
						 | 
					7b5fec1842 | ||
| 
						 | 
					5762ded601 | ||
| 
						 | 
					a3abb86daa | ||
| 
						 | 
					4f5c656b05 | ||
| 
						 | 
					a31cddbe7b | ||
| 
						 | 
					b4ecd93f1c | ||
| 
						 | 
					0acc23e058 | ||
| 
						 | 
					cdd5f9b628 | ||
| 
						 | 
					4c9f5f4655 | ||
| 
						 | 
					b80ba13cb4 | ||
| 
						 | 
					8260bdc09c | ||
| 
						 | 
					24f856e02b | ||
| 
						 | 
					3aa619b928 | ||
| 
						 | 
					4cb5e98d94 | ||
| 
						 | 
					272910575e | ||
| 
						 | 
					a15a62f4bc | ||
| 
						 | 
					53cf11db8c | ||
| 
						 | 
					01052fbe47 | ||
| 
						 | 
					a5e1e075c7 | ||
| 
						 | 
					6be32ac688 | ||
| 
						 | 
					b362c0ef38 | ||
| 
						 | 
					bba9969e31 | ||
| 
						 | 
					007ba24809 | ||
| 
						 | 
					df21539311 | ||
| 
						 | 
					2592cb6019 | ||
| 
						 | 
					f7df17a7ed | ||
| 
						 | 
					62f42b72f8 | ||
| 
						 | 
					a1ba4fda6f | ||
| 
						 | 
					1c06b04c45 | ||
| 
						 | 
					2ee22fd374 | ||
| 
						 | 
					4c230d9e61 | ||
| 
						 | 
					727294fbbe | ||
| 
						 | 
					478c43969b | ||
| 
						 | 
					79b5303350 | ||
| 
						 | 
					ce4b742b25 | ||
| 
						 | 
					a9dc15bda5 | ||
| 
						 | 
					ba6387ff5c | ||
| 
						 | 
					8fa98508b7 | ||
| 
						 | 
					decdbaecf9 | ||
| 
						 | 
					6d87cf9be0 | ||
| 
						 | 
					94f434c4a6 | ||
| 
						 | 
					7ba867c30b | ||
| 
						 | 
					3424395e10 | ||
| 
						 | 
					926c7359a2 | ||
| 
						 | 
					ec0af99a2e | ||
| 
						 | 
					b4d948886c | ||
| 
						 | 
					4d8d79372a | ||
| 
						 | 
					04a589722c | ||
| 
						 | 
					d4a10e2873 | ||
| 
						 | 
					4998ad6c7e | ||
| 
						 | 
					a07ca5ff50 | ||
| 
						 | 
					f07e7571ab | ||
| 
						 | 
					834c16485c | ||
| 
						 | 
					04a4265ef3 | ||
| 
						 | 
					0ec473195d | ||
| 
						 | 
					0bf09256b0 | ||
| 
						 | 
					db8fd2c913 | ||
| 
						 | 
					dbe6e5b3d7 | ||
| 
						 | 
					cc81cd446b | ||
| 
						 | 
					439c7118f1 | ||
| 
						 | 
					d8154a5815 | ||
| 
						 | 
					4e3787bc0d | ||
| 
						 | 
					02e0955924 | ||
| 
						 | 
					a78950e822 | ||
| 
						 | 
					1ce1a94a35 | ||
| 
						 | 
					977b6d9f67 | ||
| 
						 | 
					b5e6dbd797 | ||
| 
						 | 
					833e6688f1 | ||
| 
						 | 
					bc22c9f84f | ||
| 
						 | 
					2149a7d116 | ||
| 
						 | 
					29175d2c17 | ||
| 
						 | 
					803454d5c8 | ||
| 
						 | 
					36cf32dc42 | ||
| 
						 | 
					657f4ab303 | ||
| 
						 | 
					ea6552615d | ||
| 
						 | 
					4bf3287fce | ||
| 
						 | 
					832c2034c2 | ||
| 
						 | 
					b0aa26e1f1 | ||
| 
						 | 
					e52baeb967 | ||
| 
						 | 
					8268eb9a83 | ||
| 
						 | 
					3cc458abd9 | ||
| 
						 | 
					337b4c4268 | ||
| 
						 | 
					001f8657f6 | ||
| 
						 | 
					ea884e7fa1 | ||
| 
						 | 
					1b1394cf5d | ||
| 
						 | 
					1eef930dbb | ||
| 
						 | 
					1e175e74ed | ||
| 
						 | 
					75a46c365e | ||
| 
						 | 
					8e7b8825f5 | ||
| 
						 | 
					2ecbca303b | ||
| 
						 | 
					8195a4d616 | ||
| 
						 | 
					7ba40f925f | ||
| 
						 | 
					345cd1795f | ||
| 
						 | 
					959aaee045 | ||
| 
						 | 
					53477f0f59 | ||
| 
						 | 
					5716218f41 | ||
| 
						 | 
					9df6b9d5c0 | ||
| 
						 | 
					ec46031d36 | ||
| 
						 | 
					55b84d166a | ||
| 
						 | 
					34ae8bacec | ||
| 
						 | 
					cb4e5ca0f7 | ||
| 
						 | 
					0ba45468c4 | ||
| 
						 | 
					710502784e | ||
| 
						 | 
					0275a8558d | ||
| 
						 | 
					58acc75cf6 | ||
| 
						 | 
					874ababb9f | ||
| 
						 | 
					3771e6b0cd | ||
| 
						 | 
					33eaefa966 | ||
| 
						 | 
					cd7e236d57 | ||
| 
						 | 
					54c0b7c7d5 | ||
| 
						 | 
					a2177daec2 | ||
| 
						 | 
					628386b453 | ||
| 
						 | 
					b222bfb3e0 | ||
| 
						 | 
					ab199d883d | ||
| 
						 | 
					356065d1ee | ||
| 
						 | 
					76e7c5623d | ||
| 
						 | 
					085fba050a | ||
| 
						 | 
					295334d3ac | ||
| 
						 | 
					36124ddca4 | ||
| 
						 | 
					bd6585765e | ||
| 
						 | 
					c325deb4ed | ||
| 
						 | 
					73bb0b10ee | ||
| 
						 | 
					72820b162c | ||
| 
						 | 
					89e5b8d057 | ||
| 
						 | 
					da4f53ebbb | ||
| 
						 | 
					8458553b74 | ||
| 
						 | 
					55ecc41d06 | ||
| 
						 | 
					28fcdf2cbb | ||
| 
						 | 
					24087679a8 | ||
| 
						 | 
					5ac6a8cb4a | ||
| 
						 | 
					668d85d14e | ||
| 
						 | 
					c11a3dc95c | ||
| 
						 | 
					56f57c20a2 | ||
| 
						 | 
					240d14779a | ||
| 
						 | 
					3550d1e61c | ||
| 
						 | 
					6513ad249c | ||
| 
						 | 
					50297b1880 | ||
| 
						 | 
					f189b78b9e | ||
| 
						 | 
					5c0250f495 | ||
| 
						 | 
					2093f726e9 | ||
| 
						 | 
					10efe3859d | ||
| 
						 | 
					6933bcf7bb | ||
| 
						 | 
					2ea046cd80 | ||
| 
						 | 
					f4097a372b | ||
| 
						 | 
					87ea2a2bef | ||
| 
						 | 
					cc14a1c361 | ||
| 
						 | 
					bcdface60d | ||
| 
						 | 
					4dc9419d2e | ||
| 
						 | 
					d2bcac813e | ||
| 
						 | 
					080c37a7f6 | ||
| 
						 | 
					f9a3838db6 | ||
| 
						 | 
					1e61db104b | ||
| 
						 | 
					30a9c7718d | ||
| 
						 | 
					34b052b5d3 | ||
| 
						 | 
					aaa12853ad | ||
| 
						 | 
					b0ab55b0bf | ||
| 
						 | 
					d2f8496f4e | ||
| 
						 | 
					1a69b16d36 | ||
| 
						 | 
					b5e8673e62 | ||
| 
						 | 
					264c6a50b6 | ||
| 
						 | 
					493642eb38 | ||
| 
						 | 
					28d42b9164 | ||
| 
						 | 
					42f29062ca | ||
| 
						 | 
					c4377ed6c2 | ||
| 
						 | 
					7d283ed65f | ||
| 
						 | 
					bf1f941e50 | ||
| 
						 | 
					789fef34ba | ||
| 
						 | 
					1daf5a611c | ||
| 
						 | 
					6aed1db67e | ||
| 
						 | 
					cf68854770 | ||
| 
						 | 
					711392c73b | ||
| 
						 | 
					9573c32481 | ||
| 
						 | 
					a15f80f79d | ||
| 
						 | 
					23e7475f06 | ||
| 
						 | 
					1eb571b787 | ||
| 
						 | 
					dd3b716d85 | ||
| 
						 | 
					28649c07e3 | ||
| 
						 | 
					961e02be0d | ||
| 
						 | 
					a161491bfd | ||
| 
						 | 
					e0b4d1c1e4 | ||
| 
						 | 
					fd4aaab137 | ||
| 
						 | 
					42d14d5ca2 | ||
| 
						 | 
					d3ff482c9b | ||
| 
						 | 
					f682368eeb | ||
| 
						 | 
					4a5d033efb | ||
| 
						 | 
					343161b195 | ||
| 
						 | 
					bc576a9659 | ||
| 
						 | 
					19e407fcc4 | ||
| 
						 | 
					bc7327d004 | ||
| 
						 | 
					666fa1c797 | ||
| 
						 | 
					0eda4a7821 | ||
| 
						 | 
					862058fd2b | ||
| 
						 | 
					69e5bcd57d | ||
| 
						 | 
					efeddda328 | ||
| 
						 | 
					ff6938280e | ||
| 
						 | 
					1e4425b30f | ||
| 
						 | 
					b5d1d8cdad | ||
| 
						 | 
					029be5ccca | ||
| 
						 | 
					29c2d785b5 | ||
| 
						 | 
					abda8cfa32 | ||
| 
						 | 
					44e7d79d4c | ||
| 
						 | 
					9a1dc8ee0e | ||
| 
						 | 
					27879c3f01 | ||
| 
						 | 
					29096eb5d7 | ||
| 
						 | 
					a573baea03 | ||
| 
						 | 
					5af07c4531 | ||
| 
						 | 
					44e36feb09 | ||
| 
						 | 
					2a7d996881 | ||
| 
						 | 
					738f943a68 | ||
| 
						 | 
					47e62a5681 | ||
| 
						 | 
					1ecbfd7590 | ||
| 
						 | 
					67c139a04b | ||
| 
						 | 
					31cc008249 | ||
| 
						 | 
					9cb026439d | ||
| 
						 | 
					e6f10176c6 | ||
| 
						 | 
					0917c79470 | ||
| 
						 | 
					597baa986d | ||
| 
						 | 
					75cc4b4843 | ||
| 
						 | 
					aac088d496 | ||
| 
						 | 
					a822e5bbc5 | ||
| 
						 | 
					c527249c21 | ||
| 
						 | 
					9ef798f534 | ||
| 
						 | 
					e69b99f089 | ||
| 
						 | 
					55b8079e86 | ||
| 
						 | 
					e272dbe9af | ||
| 
						 | 
					962f8354ac | ||
| 
						 | 
					20e4a960f7 | ||
| 
						 | 
					82249cb50a | ||
| 
						 | 
					fad417e553 | ||
| 
						 | 
					5ba692f50c | 
							
								
								
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -3,6 +3,10 @@
 | 
			
		||||
/local.properties
 | 
			
		||||
.idea/
 | 
			
		||||
/build
 | 
			
		||||
app/app-release.apk
 | 
			
		||||
app/release
 | 
			
		||||
*.hprof
 | 
			
		||||
app/.externalNativeBuild/
 | 
			
		||||
.externalNativeBuild/
 | 
			
		||||
*.sh
 | 
			
		||||
public.certificate.x509.pem
 | 
			
		||||
private.key.pk8
 | 
			
		||||
*.apk
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,2 @@
 | 
			
		||||
# Magisk Manager
 | 
			
		||||
I used Java 8 features in the app, and official supported is added in Android Studio 2.4  
 | 
			
		||||
Aware that Android Studio 2.4 is currently in the Preview Channel  
 | 
			
		||||
You need to install CMake and NDK to build the zipadjust library for zip preprocessing
 | 
			
		||||
This repo is no longer an independent component. It is a submodule of the [Magisk Project](https://github.com/topjohnwu/Magisk).
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								app/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								app/.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -1 +0,0 @@
 | 
			
		||||
/build
 | 
			
		||||
@@ -1,68 +0,0 @@
 | 
			
		||||
apply plugin: 'com.android.application'
 | 
			
		||||
apply plugin: 'me.tatarka.retrolambda'
 | 
			
		||||
 | 
			
		||||
android {
 | 
			
		||||
    compileSdkVersion 25
 | 
			
		||||
    buildToolsVersion "25.0.3"
 | 
			
		||||
 | 
			
		||||
    defaultConfig {
 | 
			
		||||
        applicationId "com.topjohnwu.magisk"
 | 
			
		||||
        minSdkVersion 21
 | 
			
		||||
        targetSdkVersion 25
 | 
			
		||||
        versionCode 31
 | 
			
		||||
        versionName "4.3.3"
 | 
			
		||||
        ndk {
 | 
			
		||||
            moduleName 'zipadjust'
 | 
			
		||||
            abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    buildTypes {
 | 
			
		||||
        release {
 | 
			
		||||
            minifyEnabled true
 | 
			
		||||
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    compileOptions {
 | 
			
		||||
        sourceCompatibility JavaVersion.VERSION_1_8
 | 
			
		||||
        targetCompatibility JavaVersion.VERSION_1_8
 | 
			
		||||
    }
 | 
			
		||||
    dexOptions {
 | 
			
		||||
        preDexLibraries = true
 | 
			
		||||
    }
 | 
			
		||||
    externalNativeBuild {
 | 
			
		||||
        cmake {
 | 
			
		||||
            path 'src/main/jni/CMakeLists.txt'
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    lintOptions {
 | 
			
		||||
        disable 'MissingTranslation'
 | 
			
		||||
    }
 | 
			
		||||
    retrolambda {
 | 
			
		||||
        javaVersion JavaVersion.VERSION_1_7
 | 
			
		||||
        defaultMethods false
 | 
			
		||||
        incremental true
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
repositories {
 | 
			
		||||
    jcenter()
 | 
			
		||||
    maven { url "https://jitpack.io" }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
dependencies {
 | 
			
		||||
    compile fileTree(include: ['*.jar'], dir: 'libs')
 | 
			
		||||
 | 
			
		||||
    compile 'com.android.support:recyclerview-v7:25.3.1'
 | 
			
		||||
    compile 'com.android.support:cardview-v7:25.3.1'
 | 
			
		||||
    compile 'com.android.support:design:25.3.1'
 | 
			
		||||
    compile 'com.android.support:support-v4:25.3.1'
 | 
			
		||||
    compile 'com.jakewharton:butterknife:8.5.1'
 | 
			
		||||
    compile 'com.thoughtbot:expandablerecyclerview:1.4'
 | 
			
		||||
    compile 'us.feras.mdv:markdownview:1.1.0'
 | 
			
		||||
    compile 'com.madgag.spongycastle:core:1.54.0.0'
 | 
			
		||||
    compile 'com.madgag.spongycastle:prov:1.54.0.0'
 | 
			
		||||
    compile 'com.madgag.spongycastle:pkix:1.54.0.0'
 | 
			
		||||
    compile 'com.madgag.spongycastle:pg:1.54.0.0'
 | 
			
		||||
    compile 'com.google.android.gms:play-services-safetynet:9.0.1'
 | 
			
		||||
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
 | 
			
		||||
}
 | 
			
		||||
@@ -1,133 +0,0 @@
 | 
			
		||||
[ -z $BOOTMODE ] && BOOTMODE=false
 | 
			
		||||
 | 
			
		||||
# This path should work in any cases
 | 
			
		||||
TMPDIR=/dev/tmp
 | 
			
		||||
 | 
			
		||||
BOOTTMP=$TMPDIR/boottmp
 | 
			
		||||
MAGISKBIN=/data/magisk
 | 
			
		||||
CHROMEDIR=$MAGISKBIN/chromeos
 | 
			
		||||
 | 
			
		||||
SYSTEMLIB=/system/lib
 | 
			
		||||
[ -d /system/lib64 ] && SYSTEMLIB=/system/lib64
 | 
			
		||||
 | 
			
		||||
# Default permissions
 | 
			
		||||
umask 022
 | 
			
		||||
 | 
			
		||||
ui_print_wrapper() {
 | 
			
		||||
  type ui_print >/dev/null && ui_print "$1" || echo "$1"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
grep_prop() {
 | 
			
		||||
  REGEX="s/^$1=//p"
 | 
			
		||||
  shift
 | 
			
		||||
  FILES=$@
 | 
			
		||||
  if [ -z "$FILES" ]; then
 | 
			
		||||
    FILES='/system/build.prop'
 | 
			
		||||
  fi
 | 
			
		||||
  cat $FILES 2>/dev/null | sed -n "$REGEX" | head -n 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
find_boot_image() {
 | 
			
		||||
  if [ -z "$BOOTIMAGE" ]; then
 | 
			
		||||
    for PARTITION in kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do
 | 
			
		||||
      BOOTIMAGE=`readlink /dev/block/by-name/$PARTITION || readlink /dev/block/platform/*/by-name/$PARTITION || readlink /dev/block/platform/*/*/by-name/$PARTITION`
 | 
			
		||||
      if [ ! -z "$BOOTIMAGE" ]; then break; fi
 | 
			
		||||
    done
 | 
			
		||||
  fi
 | 
			
		||||
  if [ -z "$BOOTIMAGE" ]; then
 | 
			
		||||
    FSTAB="/etc/recovery.fstab"
 | 
			
		||||
    [ ! -f "$FSTAB" ] && FSTAB="/etc/recovery.fstab.bak"
 | 
			
		||||
    [ -f "$FSTAB" ] && BOOTIMAGE=`grep -E '\b/boot\b' "$FSTAB" | grep -oE '/dev/[a-zA-Z0-9_./-]*'`
 | 
			
		||||
  fi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# Environments
 | 
			
		||||
# Set permissions
 | 
			
		||||
chmod -R 755 $CHROMEDIR/futility $MAGISKBIN 2>/dev/null
 | 
			
		||||
# Temporary busybox for installation
 | 
			
		||||
mkdir -p $TMPDIR/busybox
 | 
			
		||||
$MAGISKBIN/busybox --install -s $TMPDIR/busybox
 | 
			
		||||
rm -f $TMPDIR/busybox/su $TMPDIR/busybox/sh $TMPDIR/busybox/reboot
 | 
			
		||||
PATH=$TMPDIR/busybox:$PATH
 | 
			
		||||
 | 
			
		||||
# Find the boot image
 | 
			
		||||
find_boot_image
 | 
			
		||||
if [ -z "$BOOTIMAGE" ]; then
 | 
			
		||||
  ui_print_wrapper "! Unable to detect boot image"
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
ui_print_wrapper "- Found Boot Image: $BOOTIMAGE"
 | 
			
		||||
 | 
			
		||||
rm -rf $BOOTTMP 2>/dev/null
 | 
			
		||||
mkdir -p $BOOTTMP
 | 
			
		||||
cd $BOOTTMP
 | 
			
		||||
 | 
			
		||||
ui_print_wrapper "- Unpacking boot image"
 | 
			
		||||
LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --unpack $BOOTIMAGE
 | 
			
		||||
if [ $? -ne 0 ]; then
 | 
			
		||||
  ui_print_wrapper "! Unable to unpack boot image"
 | 
			
		||||
  exit 1
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Update our previous backup to new format if exists
 | 
			
		||||
if [ -f /data/stock_boot.img ]; then
 | 
			
		||||
  SHA1=`LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --sha1 /data/stock_boot.img | tail -n 1`
 | 
			
		||||
  STOCKDUMP=/data/stock_boot_${SHA1}.img
 | 
			
		||||
  mv /data/stock_boot.img $STOCKDUMP
 | 
			
		||||
  LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --compress $STOCKDUMP
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
# Detect boot image state
 | 
			
		||||
LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --cpio-test ramdisk.cpio
 | 
			
		||||
case $? in
 | 
			
		||||
  0 )
 | 
			
		||||
    ui_print_wrapper "! Magisk is not installed!"
 | 
			
		||||
    ui_print_wrapper "! Nothing to uninstall"
 | 
			
		||||
    exit
 | 
			
		||||
    ;;
 | 
			
		||||
  1 )
 | 
			
		||||
    # Find SHA1 of stock boot image
 | 
			
		||||
    if [ -z $SHA1 ]; then
 | 
			
		||||
      LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --cpio-extract ramdisk.cpio init.magisk.rc init.magisk.rc
 | 
			
		||||
      SHA1=`grep_prop "# STOCKSHA1" init.magisk.rc`
 | 
			
		||||
      [ ! -z $SHA1 ] && STOCKDUMP=/data/stock_boot_${SHA1}.img
 | 
			
		||||
      rm -f init.magisk.rc
 | 
			
		||||
    fi
 | 
			
		||||
    if [ -f ${STOCKDUMP}.gz ]; then
 | 
			
		||||
      ui_print_wrapper "- Boot image backup found!"
 | 
			
		||||
      LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --decompress ${STOCKDUMP}.gz stock_boot.img
 | 
			
		||||
    else
 | 
			
		||||
      ui_print_wrapper "! Boot image backup unavailable"
 | 
			
		||||
      ui_print_wrapper "- Restoring ramdisk with backup"
 | 
			
		||||
      LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --cpio-restore ramdisk.cpio
 | 
			
		||||
      LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --repack $BOOTIMAGE stock_boot.img
 | 
			
		||||
    fi
 | 
			
		||||
    ;;
 | 
			
		||||
  2 )
 | 
			
		||||
    ui_print_wrapper "- SuperSU patched image detected"
 | 
			
		||||
    LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --cpio-restore ramdisk.cpio
 | 
			
		||||
    LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --repack $BOOTIMAGE stock_boot.img
 | 
			
		||||
    ;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
# Sign chromeos boot
 | 
			
		||||
if [ -f chromeos ]; then
 | 
			
		||||
  echo > config
 | 
			
		||||
  echo > bootloader
 | 
			
		||||
  LD_LIBRARY_PATH=$SYSTEMLIB $CHROMEDIR/futility vbutil_kernel --pack stock_boot.img.signed --keyblock $CHROMEDIR/kernel.keyblock --signprivate $CHROMEDIR/kernel_data_key.vbprivk --version 1 --vmlinuz stock_boot.img --config config --arch arm --bootloader bootloader --flags 0x1
 | 
			
		||||
  rm -f stock_boot.img
 | 
			
		||||
  mv stock_boot.img.signed stock_boot.img
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
ui_print_wrapper "- Flashing stock/reverted image"
 | 
			
		||||
[ ! -L "$BOOTIMAGE" ] && dd if=/dev/zero of=$BOOTIMAGE bs=4096 2>/dev/null
 | 
			
		||||
dd if=stock_boot.img of=$BOOTIMAGE bs=4096
 | 
			
		||||
 | 
			
		||||
ui_print_wrapper "- Removing Magisk files"
 | 
			
		||||
rm -rf  /cache/magisk.log /cache/last_magisk.log /cache/magiskhide.log /cache/.disable_magisk \
 | 
			
		||||
        /cache/magisk /cache/magisk_merge /cache/magisk_mount  /cache/unblock /cache/magisk_uninstaller.sh \
 | 
			
		||||
        /data/Magisk.apk /data/magisk.apk /data/magisk.img /data/magisk_merge.img \
 | 
			
		||||
        /data/busybox /data/magisk /data/custom_ramdisk_patch.sh 2>/dev/null
 | 
			
		||||
 | 
			
		||||
$BOOTMODE && reboot
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							@@ -1,27 +0,0 @@
 | 
			
		||||
-----BEGIN CERTIFICATE-----
 | 
			
		||||
MIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD
 | 
			
		||||
VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g
 | 
			
		||||
VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE
 | 
			
		||||
AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe
 | 
			
		||||
Fw0wODAyMjkwMTMzNDZaFw0zNTA3MTcwMTMzNDZaMIGUMQswCQYDVQQGEwJVUzET
 | 
			
		||||
MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G
 | 
			
		||||
A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p
 | 
			
		||||
ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI
 | 
			
		||||
hvcNAQEBBQADggENADCCAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waM
 | 
			
		||||
qOi8qM1r03hupwqnbOYOuw+ZNVn/2T53qUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4
 | 
			
		||||
wgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ6pPQp8PcSvNQIg1QCAcy
 | 
			
		||||
4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf/gOBzAzU
 | 
			
		||||
RNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97s
 | 
			
		||||
zI5p58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkw
 | 
			
		||||
HQYDVR0OBBYEFEhZAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZ
 | 
			
		||||
AFY9JyxGrhGGBaR0GawJyowRoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE
 | 
			
		||||
CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH
 | 
			
		||||
QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG
 | 
			
		||||
CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud
 | 
			
		||||
EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHqvlozrUMRBBVEY0NqrrwFbinZa
 | 
			
		||||
J6cVosK0TyIUFf/azgMJWr+kLfcHCHJsIGnlw27drgQAvilFLAhLwn62oX6snb4Y
 | 
			
		||||
LCBOsVMR9FXYJLZW2+TcIkCRLXWG/oiVHQGo/rWuWkJgU134NDEFJCJGjDbiLCpe
 | 
			
		||||
+ZTWHdcwauTJ9pUbo8EvHRkU3cYfGmLaLfgn9gP+pWA7LFQNvXwBnDa6sppCccEX
 | 
			
		||||
31I828XzgXpJ4O+mDL1/dBd+ek8ZPUP0IgdyZm5MTYPhvVqGCHzzTy3sIeJFymwr
 | 
			
		||||
sBbmg2OAUNLEMO6nwmocSdN2ClirfxqCzJOLSDE4QyS9BAH6EhY6UFcOaE0=
 | 
			
		||||
-----END CERTIFICATE-----
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 73 KiB  | 
@@ -1,152 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.preference.PreferenceManager;
 | 
			
		||||
import android.support.annotation.Nullable;
 | 
			
		||||
import android.support.v7.app.ActionBar;
 | 
			
		||||
import android.support.v7.app.AlertDialog;
 | 
			
		||||
import android.support.v7.widget.Toolbar;
 | 
			
		||||
import android.text.Html;
 | 
			
		||||
import android.text.Spanned;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
import android.text.method.LinkMovementMethod;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.WindowManager;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.components.AboutCardRow;
 | 
			
		||||
import com.topjohnwu.magisk.components.Activity;
 | 
			
		||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Logger;
 | 
			
		||||
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
 | 
			
		||||
public class AboutActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
    private static final String DONATION_URL = "http://topjohnwu.github.io/donate";
 | 
			
		||||
    private static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382";
 | 
			
		||||
    private static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/MagiskManager";
 | 
			
		||||
 | 
			
		||||
    @BindView(R.id.toolbar) Toolbar toolbar;
 | 
			
		||||
    @BindView(R.id.app_version_info) AboutCardRow appVersionInfo;
 | 
			
		||||
    @BindView(R.id.app_changelog) AboutCardRow appChangelog;
 | 
			
		||||
    @BindView(R.id.app_developers) AboutCardRow appDevelopers;
 | 
			
		||||
    @BindView(R.id.app_translators) AboutCardRow appTranslators;
 | 
			
		||||
    @BindView(R.id.app_source_code) AboutCardRow appSourceCode;
 | 
			
		||||
    @BindView(R.id.support_thread) AboutCardRow supportThread;
 | 
			
		||||
    @BindView(R.id.donation) AboutCardRow donation;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(@Nullable Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        String theme = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("theme", "");
 | 
			
		||||
        Logger.dev("AboutActivity: Theme is " + theme);
 | 
			
		||||
        if (getApplicationContext().isDarkTheme) {
 | 
			
		||||
            setTheme(R.style.AppTheme_Dark);
 | 
			
		||||
        }
 | 
			
		||||
        setContentView(R.layout.activity_about);
 | 
			
		||||
        ButterKnife.bind(this);
 | 
			
		||||
 | 
			
		||||
        setSupportActionBar(toolbar);
 | 
			
		||||
        toolbar.setNavigationOnClickListener(view -> finish());
 | 
			
		||||
 | 
			
		||||
        ActionBar ab = getSupportActionBar();
 | 
			
		||||
        if (ab != null) {
 | 
			
		||||
            ab.setTitle(R.string.about);
 | 
			
		||||
            ab.setDisplayHomeAsUpEnabled(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        appVersionInfo.setSummary(BuildConfig.VERSION_NAME);
 | 
			
		||||
 | 
			
		||||
        String changes = null;
 | 
			
		||||
        try (InputStream is = getAssets().open("changelog.html")) {
 | 
			
		||||
            int size = is.available();
 | 
			
		||||
 | 
			
		||||
            byte[] buffer = new byte[size];
 | 
			
		||||
            is.read(buffer);
 | 
			
		||||
 | 
			
		||||
            changes = new String(buffer);
 | 
			
		||||
        } catch (IOException ignored) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        appChangelog.removeSummary();
 | 
			
		||||
        if (changes == null) {
 | 
			
		||||
            appChangelog.setVisibility(View.GONE);
 | 
			
		||||
        } else {
 | 
			
		||||
            Spanned result;
 | 
			
		||||
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
 | 
			
		||||
                result = Html.fromHtml(changes, Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE);
 | 
			
		||||
            } else {
 | 
			
		||||
                result = Html.fromHtml(changes);
 | 
			
		||||
            }
 | 
			
		||||
            appChangelog.setOnClickListener(v -> {
 | 
			
		||||
                AlertDialog d = new AlertDialogBuilder(this)
 | 
			
		||||
                        .setTitle(R.string.app_changelog)
 | 
			
		||||
                        .setMessage(result)
 | 
			
		||||
                        .setPositiveButton(android.R.string.ok, null)
 | 
			
		||||
                        .show();
 | 
			
		||||
 | 
			
		||||
                //noinspection ConstantConditions
 | 
			
		||||
                ((TextView) d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        appDevelopers.removeSummary();
 | 
			
		||||
        appDevelopers.setOnClickListener(view -> {
 | 
			
		||||
            Spanned result;
 | 
			
		||||
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
 | 
			
		||||
                result = Html.fromHtml(getString(R.string.app_developers_), Html.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE);
 | 
			
		||||
            } else {
 | 
			
		||||
                result = Html.fromHtml(getString(R.string.app_developers_));
 | 
			
		||||
            }
 | 
			
		||||
            AlertDialog d = new AlertDialogBuilder(this)
 | 
			
		||||
                    .setTitle(R.string.app_developers)
 | 
			
		||||
                    .setMessage(result)
 | 
			
		||||
                    .setPositiveButton(android.R.string.ok, null)
 | 
			
		||||
                    .create();
 | 
			
		||||
 | 
			
		||||
            d.show();
 | 
			
		||||
            //noinspection ConstantConditions
 | 
			
		||||
            ((TextView) d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance());
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        String translators = getString(R.string.translators);
 | 
			
		||||
        if (TextUtils.isEmpty(translators)) {
 | 
			
		||||
            appTranslators.setVisibility(View.GONE);
 | 
			
		||||
        } else {
 | 
			
		||||
            appTranslators.setSummary(translators);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        appSourceCode.removeSummary();
 | 
			
		||||
        appSourceCode.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(SOURCE_CODE_URL))));
 | 
			
		||||
 | 
			
		||||
        supportThread.removeSummary();
 | 
			
		||||
        supportThread.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(XDA_THREAD))));
 | 
			
		||||
 | 
			
		||||
        donation.removeSummary();
 | 
			
		||||
        donation.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(DONATION_URL))));
 | 
			
		||||
 | 
			
		||||
        setFloating();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setFloating() {
 | 
			
		||||
        boolean isTablet = getResources().getBoolean(R.bool.isTablet);
 | 
			
		||||
        if (isTablet) {
 | 
			
		||||
            WindowManager.LayoutParams params = getWindow().getAttributes();
 | 
			
		||||
            params.height = getResources().getDimensionPixelSize(R.dimen.floating_height);
 | 
			
		||||
            params.width = getResources().getDimensionPixelSize(R.dimen.floating_width);
 | 
			
		||||
            params.alpha = 1.0f;
 | 
			
		||||
            params.dimAmount = 0.6f;
 | 
			
		||||
            params.flags |= 2;
 | 
			
		||||
            getWindow().setAttributes(params);
 | 
			
		||||
            setFinishOnTouchOutside(true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,228 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.app.ProgressDialog;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.os.CountDownTimer;
 | 
			
		||||
import android.support.annotation.Nullable;
 | 
			
		||||
import android.support.design.widget.Snackbar;
 | 
			
		||||
import android.support.v7.widget.CardView;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.ArrayAdapter;
 | 
			
		||||
import android.widget.Button;
 | 
			
		||||
import android.widget.CheckBox;
 | 
			
		||||
import android.widget.Spinner;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.ProcessMagiskZip;
 | 
			
		||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.components.SnackbarMaker;
 | 
			
		||||
import com.topjohnwu.magisk.receivers.DownloadReceiver;
 | 
			
		||||
import com.topjohnwu.magisk.utils.CallbackEvent;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
import butterknife.OnClick;
 | 
			
		||||
import butterknife.Unbinder;
 | 
			
		||||
 | 
			
		||||
public class InstallFragment extends Fragment implements CallbackEvent.Listener<Void> {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private static final String UNINSTALLER = "magisk_uninstaller.sh";
 | 
			
		||||
 | 
			
		||||
    @BindView(R.id.current_version_title) TextView currentVersionTitle;
 | 
			
		||||
    @BindView(R.id.install_title) TextView installTitle;
 | 
			
		||||
    @BindView(R.id.block_spinner) Spinner spinner;
 | 
			
		||||
    @BindView(R.id.detect_bootimage) Button detectButton;
 | 
			
		||||
    @BindView(R.id.install_button) CardView installButton;
 | 
			
		||||
    @BindView(R.id.install_text) TextView installText;
 | 
			
		||||
    @BindView(R.id.uninstall_button) CardView uninstallButton;
 | 
			
		||||
    @BindView(R.id.keep_force_enc) CheckBox keepEncChkbox;
 | 
			
		||||
    @BindView(R.id.keep_verity) CheckBox keepVerityChkbox;
 | 
			
		||||
 | 
			
		||||
    @OnClick(R.id.detect_bootimage)
 | 
			
		||||
    public void toAutoDetect() {
 | 
			
		||||
        if (magiskManager.bootBlock != null) {
 | 
			
		||||
            spinner.setSelection(0);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @OnClick(R.id.install_button)
 | 
			
		||||
    public void install() {
 | 
			
		||||
        String bootImage = null;
 | 
			
		||||
        if (magiskManager.blockList != null) {
 | 
			
		||||
            int idx = spinner.getSelectedItemPosition();
 | 
			
		||||
            if (magiskManager.bootBlock != null) {
 | 
			
		||||
                if (idx > 0) {
 | 
			
		||||
                    bootImage = magiskManager.blockList.get(idx - 1);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                if (idx > 0)  {
 | 
			
		||||
                    bootImage = magiskManager.blockList.get(idx - 1);
 | 
			
		||||
                } else {
 | 
			
		||||
                    SnackbarMaker.make(getActivity(), R.string.manual_boot_image, Snackbar.LENGTH_LONG);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        final String finalBootImage = bootImage;
 | 
			
		||||
        String filename = "Magisk-v" + magiskManager.remoteMagiskVersion + ".zip";
 | 
			
		||||
        new AlertDialogBuilder(getActivity())
 | 
			
		||||
                .setTitle(getString(R.string.repo_install_title, getString(R.string.magisk)))
 | 
			
		||||
                .setMessage(getString(R.string.repo_install_msg, filename))
 | 
			
		||||
                .setCancelable(true)
 | 
			
		||||
                .setPositiveButton(Shell.rootAccess() ? R.string.install : R.string.download,
 | 
			
		||||
                    (dialogInterface, i) -> Utils.dlAndReceive(
 | 
			
		||||
                        getActivity(),
 | 
			
		||||
                        new DownloadReceiver() {
 | 
			
		||||
                            private String boot = finalBootImage;
 | 
			
		||||
                            private boolean enc = keepEncChkbox.isChecked();
 | 
			
		||||
                            private boolean verity = keepVerityChkbox.isChecked();
 | 
			
		||||
 | 
			
		||||
                            @Override
 | 
			
		||||
                            public void onDownloadDone(Uri uri) {
 | 
			
		||||
                                new ProcessMagiskZip(getActivity(), uri, boot, enc, verity).exec();
 | 
			
		||||
                            }
 | 
			
		||||
                        },
 | 
			
		||||
                        magiskManager.magiskLink,
 | 
			
		||||
                        Utils.getLegalFilename(filename)))
 | 
			
		||||
                .setNeutralButton(R.string.release_notes, (dialog, which) -> {
 | 
			
		||||
                    if (magiskManager.releaseNoteLink != null) {
 | 
			
		||||
                        Intent openReleaseNoteLink = new Intent(Intent.ACTION_VIEW, Uri.parse(magiskManager.releaseNoteLink));
 | 
			
		||||
                        openReleaseNoteLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
			
		||||
                        magiskManager.startActivity(openReleaseNoteLink);
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
                .setNegativeButton(R.string.no_thanks, null)
 | 
			
		||||
                .show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @OnClick(R.id.uninstall_button)
 | 
			
		||||
    public void uninstall() {
 | 
			
		||||
        new AlertDialogBuilder(getActivity())
 | 
			
		||||
                .setTitle(R.string.uninstall_magisk_title)
 | 
			
		||||
                .setMessage(R.string.uninstall_magisk_msg)
 | 
			
		||||
                .setPositiveButton(R.string.yes, (dialogInterface, i) -> {
 | 
			
		||||
                    try {
 | 
			
		||||
                        InputStream in = magiskManager.getAssets().open(UNINSTALLER);
 | 
			
		||||
                        File uninstaller = new File(magiskManager.getCacheDir(), UNINSTALLER);
 | 
			
		||||
                        FileOutputStream out = new FileOutputStream(uninstaller);
 | 
			
		||||
                        byte[] bytes = new byte[1024];
 | 
			
		||||
                        int read;
 | 
			
		||||
                        while ((read = in.read(bytes)) != -1) {
 | 
			
		||||
                            out.write(bytes, 0, read);
 | 
			
		||||
                        }
 | 
			
		||||
                        in.close();
 | 
			
		||||
                        out.close();
 | 
			
		||||
                        ProgressDialog progress = new ProgressDialog(getActivity());
 | 
			
		||||
                        progress.setTitle(R.string.reboot);
 | 
			
		||||
                        progress.show();
 | 
			
		||||
                        new CountDownTimer(5000, 1000) {
 | 
			
		||||
                            @Override
 | 
			
		||||
                            public void onTick(long millisUntilFinished) {
 | 
			
		||||
                                progress.setMessage(getString(R.string.reboot_countdown, millisUntilFinished / 1000));
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            @Override
 | 
			
		||||
                            public void onFinish() {
 | 
			
		||||
                                progress.setMessage(getString(R.string.reboot_countdown, 0));
 | 
			
		||||
                                Shell.su(true, "mv -f " + uninstaller + " /cache/" + UNINSTALLER,
 | 
			
		||||
                                        "reboot");
 | 
			
		||||
                            }
 | 
			
		||||
                        }.start();
 | 
			
		||||
                    } catch (IOException e) {
 | 
			
		||||
                        e.printStackTrace();
 | 
			
		||||
                    }
 | 
			
		||||
                })
 | 
			
		||||
                .setNegativeButton(R.string.no_thanks, null)
 | 
			
		||||
                .show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Unbinder unbinder;
 | 
			
		||||
    private MagiskManager magiskManager;
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
 | 
			
		||||
        View v = inflater.inflate(R.layout.fragment_install, container, false);
 | 
			
		||||
        unbinder = ButterKnife.bind(this, v);
 | 
			
		||||
        magiskManager = getApplication();
 | 
			
		||||
        if (magiskManager.magiskVersion < 0) {
 | 
			
		||||
            currentVersionTitle.setText(getString(R.string.current_magisk_title, getString(R.string.version_none)));
 | 
			
		||||
        } else {
 | 
			
		||||
            currentVersionTitle.setText(getString(R.string.current_magisk_title, "v" + magiskManager.magiskVersionString));
 | 
			
		||||
        }
 | 
			
		||||
        installTitle.setText(getString(R.string.install_magisk_title, "v" + String.format(Locale.US, "%.1f", magiskManager.remoteMagiskVersion)));
 | 
			
		||||
 | 
			
		||||
        updateUI();
 | 
			
		||||
        return v;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTrigger(CallbackEvent<Void> event) {
 | 
			
		||||
        updateUI();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void updateUI() {
 | 
			
		||||
        if (magiskManager.blockList == null || !Shell.rootAccess()) {
 | 
			
		||||
            uninstallButton.setVisibility(View.GONE);
 | 
			
		||||
            installText.setText(R.string.download);
 | 
			
		||||
            detectButton.setEnabled(false);
 | 
			
		||||
            keepEncChkbox.setEnabled(false);
 | 
			
		||||
            keepVerityChkbox.setEnabled(false);
 | 
			
		||||
            spinner.setEnabled(false);
 | 
			
		||||
        } else {
 | 
			
		||||
            uninstallButton.setVisibility(magiskManager.magiskVersion > 10.3 ? View.VISIBLE : View.GONE);
 | 
			
		||||
            installText.setText(R.string.download_install);
 | 
			
		||||
            detectButton.setEnabled(true);
 | 
			
		||||
            keepEncChkbox.setEnabled(true);
 | 
			
		||||
            keepVerityChkbox.setEnabled(true);
 | 
			
		||||
            spinner.setEnabled(true);
 | 
			
		||||
 | 
			
		||||
            List<String> items = new ArrayList<>();
 | 
			
		||||
            if (magiskManager.bootBlock != null) {
 | 
			
		||||
                items.add(getString(R.string.auto_detect, magiskManager.bootBlock));
 | 
			
		||||
            } else {
 | 
			
		||||
                items.add(getString(R.string.cannot_auto_detect));
 | 
			
		||||
            }
 | 
			
		||||
            items.addAll(magiskManager.blockList);
 | 
			
		||||
            ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(),
 | 
			
		||||
                    android.R.layout.simple_spinner_item, items);
 | 
			
		||||
            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
 | 
			
		||||
            spinner.setAdapter(adapter);
 | 
			
		||||
            toAutoDetect();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onStart() {
 | 
			
		||||
        super.onStart();
 | 
			
		||||
        getActivity().setTitle(R.string.install);
 | 
			
		||||
        magiskManager.blockDetectionDone.register(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onStop() {
 | 
			
		||||
        magiskManager.blockDetectionDone.unRegister(this);
 | 
			
		||||
        super.onStop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onDestroyView() {
 | 
			
		||||
        super.onDestroyView();
 | 
			
		||||
        unbinder.unbind();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,179 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.app.Application;
 | 
			
		||||
import android.content.SharedPreferences;
 | 
			
		||||
import android.content.pm.ApplicationInfo;
 | 
			
		||||
import android.os.Handler;
 | 
			
		||||
import android.preference.PreferenceManager;
 | 
			
		||||
import android.util.SparseArray;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.module.Module;
 | 
			
		||||
import com.topjohnwu.magisk.module.Repo;
 | 
			
		||||
import com.topjohnwu.magisk.superuser.Policy;
 | 
			
		||||
import com.topjohnwu.magisk.utils.CallbackEvent;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.magisk.utils.ValueSortedMap;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class MagiskManager extends Application {
 | 
			
		||||
 | 
			
		||||
    public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk";
 | 
			
		||||
    public static final String MAGISK_HIDE_PATH = "/magisk/.core/magiskhide/";
 | 
			
		||||
    public static final String TMP_FOLDER_PATH = "/dev/tmp";
 | 
			
		||||
    public static final String MAGISK_PATH = "/magisk";
 | 
			
		||||
    public static final String INTENT_SECTION = "section";
 | 
			
		||||
 | 
			
		||||
    // Events
 | 
			
		||||
    public final CallbackEvent<Void> blockDetectionDone = new CallbackEvent<>();
 | 
			
		||||
    public final CallbackEvent<Void> magiskHideDone = new CallbackEvent<>();
 | 
			
		||||
    public final CallbackEvent<Void> reloadMainActivity = new CallbackEvent<>();
 | 
			
		||||
    public final CallbackEvent<Void> moduleLoadDone = new CallbackEvent<>();
 | 
			
		||||
    public final CallbackEvent<Void> repoLoadDone = new CallbackEvent<>();
 | 
			
		||||
    public final CallbackEvent<Void> updateCheckDone = new CallbackEvent<>();
 | 
			
		||||
    public final CallbackEvent<Void> safetyNetDone = new CallbackEvent<>();
 | 
			
		||||
    public final SparseArray<CallbackEvent<Policy>> uidSuRequest = new SparseArray<>();
 | 
			
		||||
 | 
			
		||||
    // Info
 | 
			
		||||
    public double magiskVersion;
 | 
			
		||||
    public String magiskVersionString;
 | 
			
		||||
    public double remoteMagiskVersion = -1;
 | 
			
		||||
    public String magiskLink;
 | 
			
		||||
    public String releaseNoteLink;
 | 
			
		||||
    public int SNCheckResult = -1;
 | 
			
		||||
    public String bootBlock = null;
 | 
			
		||||
    public boolean isSuClient = false;
 | 
			
		||||
    public String suVersion = null;
 | 
			
		||||
    public boolean disabled;
 | 
			
		||||
    public boolean magiskHideStarted;
 | 
			
		||||
 | 
			
		||||
    // Data
 | 
			
		||||
    public ValueSortedMap<String, Repo> repoMap;
 | 
			
		||||
    public ValueSortedMap<String, Module> moduleMap;
 | 
			
		||||
    public List<String> blockList;
 | 
			
		||||
    public List<ApplicationInfo> appList;
 | 
			
		||||
    public List<String> magiskHideList;
 | 
			
		||||
 | 
			
		||||
    // Configurations
 | 
			
		||||
    public static boolean shellLogging;
 | 
			
		||||
    public static boolean devLogging;
 | 
			
		||||
 | 
			
		||||
    public boolean magiskHide;
 | 
			
		||||
    public boolean isDarkTheme;
 | 
			
		||||
    public boolean updateNotification;
 | 
			
		||||
    public boolean busybox;
 | 
			
		||||
    public int suRequestTimeout;
 | 
			
		||||
    public int suLogTimeout = 14;
 | 
			
		||||
    public int suAccessState;
 | 
			
		||||
    public int suResponseType;
 | 
			
		||||
    public int suNotificationType;
 | 
			
		||||
 | 
			
		||||
    public SharedPreferences prefs;
 | 
			
		||||
 | 
			
		||||
    private static Handler mHandler = new Handler();
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate() {
 | 
			
		||||
        super.onCreate();
 | 
			
		||||
        prefs = PreferenceManager.getDefaultSharedPreferences(this);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void toast(String msg, int duration) {
 | 
			
		||||
        mHandler.post(() -> Toast.makeText(this, msg, duration).show());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void toast(int resId, int duration) {
 | 
			
		||||
        mHandler.post(() -> Toast.makeText(this, resId, duration).show());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void init() {
 | 
			
		||||
        isDarkTheme = prefs.getBoolean("dark_theme", false);
 | 
			
		||||
        devLogging = prefs.getBoolean("developer_logging", false);
 | 
			
		||||
        shellLogging = prefs.getBoolean("shell_logging", false);
 | 
			
		||||
        magiskHide = prefs.getBoolean("magiskhide", false);
 | 
			
		||||
        updateNotification = prefs.getBoolean("notification", true);
 | 
			
		||||
        // Always start a new root shell manually, just for safety
 | 
			
		||||
        Shell.init();
 | 
			
		||||
        updateMagiskInfo();
 | 
			
		||||
        initSuAccess();
 | 
			
		||||
        initSuConfigs();
 | 
			
		||||
        // Initialize prefs
 | 
			
		||||
        prefs.edit()
 | 
			
		||||
                .putBoolean("dark_theme", isDarkTheme)
 | 
			
		||||
                .putBoolean("magiskhide", magiskHide)
 | 
			
		||||
                .putBoolean("notification", updateNotification)
 | 
			
		||||
                .putBoolean("busybox", busybox)
 | 
			
		||||
                .putBoolean("hosts", new File("/magisk/.core/hosts").exists())
 | 
			
		||||
                .putBoolean("disable", Utils.itemExist(MAGISK_DISABLE_FILE))
 | 
			
		||||
                .putString("su_request_timeout", String.valueOf(suRequestTimeout))
 | 
			
		||||
                .putString("su_auto_response", String.valueOf(suResponseType))
 | 
			
		||||
                .putString("su_notification", String.valueOf(suNotificationType))
 | 
			
		||||
                .putString("su_access", String.valueOf(suAccessState))
 | 
			
		||||
                .apply();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void initSuConfigs() {
 | 
			
		||||
        suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10);
 | 
			
		||||
        suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0);
 | 
			
		||||
        suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void initSuAccess() {
 | 
			
		||||
        List<String> ret = Shell.sh("su -v");
 | 
			
		||||
        if (Utils.isValidShellResponse(ret)) {
 | 
			
		||||
            suVersion = ret.get(0);
 | 
			
		||||
            isSuClient = suVersion.toUpperCase().contains("MAGISK");
 | 
			
		||||
        }
 | 
			
		||||
        if (isSuClient) {
 | 
			
		||||
            ret = Shell.sh("getprop persist.sys.root_access");
 | 
			
		||||
            if (Utils.isValidShellResponse(ret)) {
 | 
			
		||||
                suAccessState = Integer.parseInt(ret.get(0));
 | 
			
		||||
            } else {
 | 
			
		||||
                Shell.su(true, "setprop persist.sys.root_access 3");
 | 
			
		||||
                suAccessState = 3;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void updateMagiskInfo() {
 | 
			
		||||
        List<String> ret = Shell.sh("getprop magisk.version");
 | 
			
		||||
        if (!Utils.isValidShellResponse(ret)) {
 | 
			
		||||
            magiskVersion = -1;
 | 
			
		||||
        } else {
 | 
			
		||||
            try {
 | 
			
		||||
                magiskVersionString = ret.get(0);
 | 
			
		||||
                magiskVersion = Double.parseDouble(ret.get(0));
 | 
			
		||||
            } catch (NumberFormatException e) {
 | 
			
		||||
                // Custom version don't need to receive updates
 | 
			
		||||
                magiskVersion = Double.POSITIVE_INFINITY;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        ret = Shell.sh("getprop persist.magisk.busybox");
 | 
			
		||||
        try {
 | 
			
		||||
            busybox = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
 | 
			
		||||
        } catch (NumberFormatException e) {
 | 
			
		||||
            busybox = false;
 | 
			
		||||
        }
 | 
			
		||||
        ret = Shell.sh("getprop ro.magisk.disable");
 | 
			
		||||
        try {
 | 
			
		||||
            disabled = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
 | 
			
		||||
        } catch (NumberFormatException e) {
 | 
			
		||||
            disabled = false;
 | 
			
		||||
        }
 | 
			
		||||
        ret = Shell.sh("getprop persist.magisk.hide");
 | 
			
		||||
        try {
 | 
			
		||||
            magiskHideStarted = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
 | 
			
		||||
        } catch (NumberFormatException e) {
 | 
			
		||||
            magiskHideStarted = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (magiskHideStarted) {
 | 
			
		||||
            magiskHide = true;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,216 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.support.annotation.Nullable;
 | 
			
		||||
import android.support.v4.view.MenuItemCompat;
 | 
			
		||||
import android.support.v4.widget.SwipeRefreshLayout;
 | 
			
		||||
import android.support.v7.widget.RecyclerView;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.Menu;
 | 
			
		||||
import android.view.MenuInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.SearchView;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.adapters.ReposAdapter;
 | 
			
		||||
import com.topjohnwu.magisk.adapters.SimpleSectionedRecyclerViewAdapter;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.LoadRepos;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.ParallelTask;
 | 
			
		||||
import com.topjohnwu.magisk.components.Fragment;
 | 
			
		||||
import com.topjohnwu.magisk.module.Module;
 | 
			
		||||
import com.topjohnwu.magisk.module.Repo;
 | 
			
		||||
import com.topjohnwu.magisk.utils.CallbackEvent;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Logger;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
import butterknife.Unbinder;
 | 
			
		||||
 | 
			
		||||
public class ReposFragment extends Fragment implements CallbackEvent.Listener<Void> {
 | 
			
		||||
 | 
			
		||||
    private Unbinder unbinder;
 | 
			
		||||
    @BindView(R.id.recyclerView) RecyclerView recyclerView;
 | 
			
		||||
    @BindView(R.id.empty_rv) TextView emptyRv;
 | 
			
		||||
    @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
 | 
			
		||||
 | 
			
		||||
    private List<Repo> mUpdateRepos = new ArrayList<>();
 | 
			
		||||
    private List<Repo> mInstalledRepos = new ArrayList<>();
 | 
			
		||||
    private List<Repo> mOthersRepos = new ArrayList<>();
 | 
			
		||||
    private List<Repo> fUpdateRepos = new ArrayList<>();
 | 
			
		||||
    private List<Repo> fInstalledRepos = new ArrayList<>();
 | 
			
		||||
    private List<Repo> fOthersRepos = new ArrayList<>();
 | 
			
		||||
 | 
			
		||||
    private SimpleSectionedRecyclerViewAdapter mSectionedAdapter;
 | 
			
		||||
 | 
			
		||||
    private SearchView.OnQueryTextListener searchListener;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreate(@Nullable Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        setHasOptionsMenu(true);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Nullable
 | 
			
		||||
    @Override
 | 
			
		||||
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
 | 
			
		||||
        View view = inflater.inflate(R.layout.fragment_repos, container, false);
 | 
			
		||||
        unbinder = ButterKnife.bind(this, view);
 | 
			
		||||
 | 
			
		||||
        mSectionedAdapter = new SimpleSectionedRecyclerViewAdapter(R.layout.section,
 | 
			
		||||
                R.id.section_text, new ReposAdapter(fUpdateRepos, fInstalledRepos, fOthersRepos));
 | 
			
		||||
 | 
			
		||||
        recyclerView.setAdapter(mSectionedAdapter);
 | 
			
		||||
 | 
			
		||||
        mSwipeRefreshLayout.setRefreshing(true);
 | 
			
		||||
 | 
			
		||||
        mSwipeRefreshLayout.setOnRefreshListener(() -> {
 | 
			
		||||
            recyclerView.setVisibility(View.GONE);
 | 
			
		||||
            new LoadRepos(getActivity()).exec();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (getApplication().repoLoadDone.isTriggered) {
 | 
			
		||||
            reloadRepos();
 | 
			
		||||
            updateUI();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        searchListener = new SearchView.OnQueryTextListener() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean onQueryTextSubmit(String query) {
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public boolean onQueryTextChange(String newText) {
 | 
			
		||||
                new FilterApps().exec(newText);
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return view;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onTrigger(CallbackEvent<Void> event) {
 | 
			
		||||
        Logger.dev("ReposFragment: UI refresh triggered");
 | 
			
		||||
        reloadRepos();
 | 
			
		||||
        updateUI();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
 | 
			
		||||
        inflater.inflate(R.menu.menu_repo, menu);
 | 
			
		||||
        SearchView search = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.repo_search));
 | 
			
		||||
        search.setOnQueryTextListener(searchListener);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onStart() {
 | 
			
		||||
        super.onStart();
 | 
			
		||||
        getApplication().repoLoadDone.register(this);
 | 
			
		||||
        getActivity().setTitle(R.string.downloads);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onStop() {
 | 
			
		||||
        getApplication().repoLoadDone.unRegister(this);
 | 
			
		||||
        super.onStop();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onDestroyView() {
 | 
			
		||||
        super.onDestroyView();
 | 
			
		||||
        unbinder.unbind();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void reloadRepos() {
 | 
			
		||||
        mUpdateRepos.clear();
 | 
			
		||||
        mInstalledRepos.clear();
 | 
			
		||||
        mOthersRepos.clear();
 | 
			
		||||
        for (Repo repo : getApplication().repoMap.values()) {
 | 
			
		||||
            Module module = getApplication().moduleMap.get(repo.getId());
 | 
			
		||||
            if (module != null) {
 | 
			
		||||
                if (repo.getVersionCode() > module.getVersionCode()) {
 | 
			
		||||
                    mUpdateRepos.add(repo);
 | 
			
		||||
                } else {
 | 
			
		||||
                    mInstalledRepos.add(repo);
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                mOthersRepos.add(repo);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        fUpdateRepos.clear();
 | 
			
		||||
        fInstalledRepos.clear();
 | 
			
		||||
        fOthersRepos.clear();
 | 
			
		||||
        fUpdateRepos.addAll(mUpdateRepos);
 | 
			
		||||
        fInstalledRepos.addAll(mInstalledRepos);
 | 
			
		||||
        fOthersRepos.addAll(mOthersRepos);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void updateUI() {
 | 
			
		||||
        if (fUpdateRepos.size() + fInstalledRepos.size() + fOthersRepos.size() == 0) {
 | 
			
		||||
            emptyRv.setVisibility(View.VISIBLE);
 | 
			
		||||
            recyclerView.setVisibility(View.GONE);
 | 
			
		||||
        } else {
 | 
			
		||||
            List<SimpleSectionedRecyclerViewAdapter.Section> sections = new ArrayList<>();
 | 
			
		||||
            if (!fUpdateRepos.isEmpty()) {
 | 
			
		||||
                sections.add(new SimpleSectionedRecyclerViewAdapter.Section(0, getString(R.string.update_available)));
 | 
			
		||||
            }
 | 
			
		||||
            if (!fInstalledRepos.isEmpty()) {
 | 
			
		||||
                sections.add(new SimpleSectionedRecyclerViewAdapter.Section(fUpdateRepos.size(), getString(R.string.installed)));
 | 
			
		||||
            }
 | 
			
		||||
            if (!fOthersRepos.isEmpty()) {
 | 
			
		||||
                sections.add(new SimpleSectionedRecyclerViewAdapter.Section(fUpdateRepos.size() + fInstalledRepos.size(), getString(R.string.not_installed)));
 | 
			
		||||
            }
 | 
			
		||||
            SimpleSectionedRecyclerViewAdapter.Section[] array = sections.toArray(new SimpleSectionedRecyclerViewAdapter.Section[sections.size()]);
 | 
			
		||||
            mSectionedAdapter.setSections(array);
 | 
			
		||||
            emptyRv.setVisibility(View.GONE);
 | 
			
		||||
            recyclerView.setVisibility(View.VISIBLE);
 | 
			
		||||
        }
 | 
			
		||||
        mSwipeRefreshLayout.setRefreshing(false);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class FilterApps extends ParallelTask<String, Void, Void> {
 | 
			
		||||
        @Override
 | 
			
		||||
        protected Void doInBackground(String... strings) {
 | 
			
		||||
            String newText = strings[0];
 | 
			
		||||
            fUpdateRepos.clear();
 | 
			
		||||
            fInstalledRepos.clear();
 | 
			
		||||
            fOthersRepos.clear();
 | 
			
		||||
            for (Repo repo: mUpdateRepos) {
 | 
			
		||||
                if (repo.getName().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        || repo.getAuthor().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        || repo.getDescription().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        ) {
 | 
			
		||||
                    fUpdateRepos.add(repo);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            for (Repo repo: mInstalledRepos) {
 | 
			
		||||
                if (repo.getName().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        || repo.getAuthor().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        || repo.getDescription().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        ) {
 | 
			
		||||
                    fInstalledRepos.add(repo);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            for (Repo repo: mOthersRepos) {
 | 
			
		||||
                if (repo.getName().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        || repo.getAuthor().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        || repo.getDescription().toLowerCase().contains(newText.toLowerCase())
 | 
			
		||||
                        ) {
 | 
			
		||||
                    fOthersRepos.add(repo);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        protected void onPostExecute(Void v) {
 | 
			
		||||
            updateUI();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,249 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.content.SharedPreferences;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
import android.preference.ListPreference;
 | 
			
		||||
import android.preference.PreferenceCategory;
 | 
			
		||||
import android.preference.PreferenceFragment;
 | 
			
		||||
import android.preference.PreferenceManager;
 | 
			
		||||
import android.preference.PreferenceScreen;
 | 
			
		||||
import android.support.v7.app.ActionBar;
 | 
			
		||||
import android.support.v7.widget.Toolbar;
 | 
			
		||||
import android.view.WindowManager;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.MagiskHide;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.SerialTask;
 | 
			
		||||
import com.topjohnwu.magisk.components.Activity;
 | 
			
		||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Logger;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
 | 
			
		||||
public class SettingsActivity extends Activity {
 | 
			
		||||
 | 
			
		||||
    @BindView(R.id.toolbar) Toolbar toolbar;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
        if (getApplicationContext().isDarkTheme) {
 | 
			
		||||
            setTheme(R.style.AppTheme_Dark);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setContentView(R.layout.activity_container);
 | 
			
		||||
        ButterKnife.bind(this);
 | 
			
		||||
 | 
			
		||||
        setSupportActionBar(toolbar);
 | 
			
		||||
 | 
			
		||||
        toolbar.setNavigationOnClickListener(view -> finish());
 | 
			
		||||
 | 
			
		||||
        ActionBar ab = getSupportActionBar();
 | 
			
		||||
        if (ab != null) {
 | 
			
		||||
            ab.setTitle(R.string.settings);
 | 
			
		||||
            ab.setDisplayHomeAsUpEnabled(true);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        setFloating();
 | 
			
		||||
 | 
			
		||||
        if (savedInstanceState == null) {
 | 
			
		||||
            getFragmentManager().beginTransaction().add(R.id.container, new SettingsFragment()).commit();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void setFloating() {
 | 
			
		||||
        boolean isTablet = getResources().getBoolean(R.bool.isTablet);
 | 
			
		||||
        if (isTablet) {
 | 
			
		||||
            WindowManager.LayoutParams params = getWindow().getAttributes();
 | 
			
		||||
            params.height = getResources().getDimensionPixelSize(R.dimen.floating_height);
 | 
			
		||||
            params.width = getResources().getDimensionPixelSize(R.dimen.floating_width);
 | 
			
		||||
            params.alpha = 1.0f;
 | 
			
		||||
            params.dimAmount = 0.6f;
 | 
			
		||||
            params.flags |= 2;
 | 
			
		||||
            getWindow().setAttributes(params);
 | 
			
		||||
            setFinishOnTouchOutside(true);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public static class SettingsFragment extends PreferenceFragment
 | 
			
		||||
            implements SharedPreferences.OnSharedPreferenceChangeListener {
 | 
			
		||||
 | 
			
		||||
        private SharedPreferences prefs;
 | 
			
		||||
        private PreferenceScreen prefScreen;
 | 
			
		||||
 | 
			
		||||
        private ListPreference suAccess, autoRes, suNotification, requestTimeout;
 | 
			
		||||
 | 
			
		||||
        private MagiskManager getApplication() {
 | 
			
		||||
            return Utils.getMagiskManager(getActivity());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
            super.onCreate(savedInstanceState);
 | 
			
		||||
            addPreferencesFromResource(R.xml.app_settings);
 | 
			
		||||
            prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
 | 
			
		||||
            prefScreen = getPreferenceScreen();
 | 
			
		||||
 | 
			
		||||
            PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk");
 | 
			
		||||
            PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser");
 | 
			
		||||
 | 
			
		||||
            suAccess = (ListPreference) findPreference("su_access");
 | 
			
		||||
            autoRes = (ListPreference) findPreference("su_auto_response");
 | 
			
		||||
            requestTimeout = (ListPreference) findPreference("su_request_timeout");
 | 
			
		||||
            suNotification = (ListPreference) findPreference("su_notification");
 | 
			
		||||
 | 
			
		||||
            setSummary();
 | 
			
		||||
 | 
			
		||||
            findPreference("clear").setOnPreferenceClickListener((pref) -> {
 | 
			
		||||
                Utils.clearRepoCache(getActivity());
 | 
			
		||||
                return true;
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            if (!Shell.rootAccess()) {
 | 
			
		||||
                prefScreen.removePreference(magiskCategory);
 | 
			
		||||
                prefScreen.removePreference(suCategory);
 | 
			
		||||
            } else {
 | 
			
		||||
                if (!getApplication().isSuClient) {
 | 
			
		||||
                    prefScreen.removePreference(suCategory);
 | 
			
		||||
                }
 | 
			
		||||
                if (getApplication().magiskVersion < 11) {
 | 
			
		||||
                    prefScreen.removePreference(magiskCategory);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onResume() {
 | 
			
		||||
            super.onResume();
 | 
			
		||||
            prefs.registerOnSharedPreferenceChangeListener(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onPause() {
 | 
			
		||||
            super.onPause();
 | 
			
		||||
            prefs.unregisterOnSharedPreferenceChangeListener(this);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
 | 
			
		||||
            Logger.dev("Settings: Prefs change " + key);
 | 
			
		||||
            boolean enabled;
 | 
			
		||||
 | 
			
		||||
            switch (key) {
 | 
			
		||||
                case "dark_theme":
 | 
			
		||||
                    enabled = prefs.getBoolean("dark_theme", false);
 | 
			
		||||
                    if (getApplication().isDarkTheme != enabled) {
 | 
			
		||||
                        getApplication().isDarkTheme = enabled;
 | 
			
		||||
                        getApplication().reloadMainActivity.trigger();
 | 
			
		||||
                        getActivity().recreate();
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case "disable":
 | 
			
		||||
                    enabled = prefs.getBoolean("disable", false);
 | 
			
		||||
                    new SerialTask<Void, Void, Void>() {
 | 
			
		||||
                        private boolean enable = enabled;
 | 
			
		||||
                        @Override
 | 
			
		||||
                        protected Void doInBackground(Void... voids) {
 | 
			
		||||
                            if (enable) {
 | 
			
		||||
                                Utils.createFile(MagiskManager.MAGISK_DISABLE_FILE);
 | 
			
		||||
                            } else {
 | 
			
		||||
                                Utils.removeItem(MagiskManager.MAGISK_DISABLE_FILE);
 | 
			
		||||
                            }
 | 
			
		||||
                            return null;
 | 
			
		||||
                        }
 | 
			
		||||
                    }.exec();
 | 
			
		||||
                    Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show();
 | 
			
		||||
                    break;
 | 
			
		||||
                case "busybox":
 | 
			
		||||
                    enabled = prefs.getBoolean("busybox", false);
 | 
			
		||||
                    new SerialTask<Void, Void, Void>() {
 | 
			
		||||
                        private boolean enable = enabled;
 | 
			
		||||
                        @Override
 | 
			
		||||
                        protected Void doInBackground(Void... voids) {
 | 
			
		||||
                            if (enable) {
 | 
			
		||||
                                Shell.su(
 | 
			
		||||
                                        "setprop persist.magisk.busybox 1",
 | 
			
		||||
                                        "sh /sbin/magic_mask.sh mount_busybox");
 | 
			
		||||
                            } else {
 | 
			
		||||
                                Shell.su(
 | 
			
		||||
                                        "setprop persist.magisk.busybox 0",
 | 
			
		||||
                                        "umount /system/xbin");
 | 
			
		||||
                            }
 | 
			
		||||
                            return null;
 | 
			
		||||
                        }
 | 
			
		||||
                    }.exec();
 | 
			
		||||
                    break;
 | 
			
		||||
                case "magiskhide":
 | 
			
		||||
                    enabled = prefs.getBoolean("magiskhide", false);
 | 
			
		||||
                    if (enabled) {
 | 
			
		||||
                        if (!getApplication().isSuClient) {
 | 
			
		||||
                            new AlertDialogBuilder(getActivity())
 | 
			
		||||
                                    .setTitle(R.string.no_magisksu_title)
 | 
			
		||||
                                    .setMessage(R.string.no_magisksu_msg)
 | 
			
		||||
                                    .setPositiveButton(R.string.understand, (dialog, which) -> new MagiskHide().enable())
 | 
			
		||||
                                    .setCancelable(false)
 | 
			
		||||
                                    .show();
 | 
			
		||||
                        } else {
 | 
			
		||||
                            new MagiskHide().enable();
 | 
			
		||||
                        }
 | 
			
		||||
                    } else {
 | 
			
		||||
                        new MagiskHide().disable();
 | 
			
		||||
                    }
 | 
			
		||||
                    break;
 | 
			
		||||
                case "hosts":
 | 
			
		||||
                    enabled = prefs.getBoolean("hosts", false);
 | 
			
		||||
                    new SerialTask<Void, Void, Void>() {
 | 
			
		||||
                        private boolean enable = enabled;
 | 
			
		||||
                        @Override
 | 
			
		||||
                        protected Void doInBackground(Void... voids) {
 | 
			
		||||
                            if (enable) {
 | 
			
		||||
                                Shell.su("cp -af /system/etc/hosts /magisk/.core/hosts",
 | 
			
		||||
                                        "mount -o bind /magisk/.core/hosts /system/etc/hosts");
 | 
			
		||||
                            } else {
 | 
			
		||||
                                Shell.su("umount -l /system/etc/hosts",
 | 
			
		||||
                                        "rm -f /magisk/.core/hosts");
 | 
			
		||||
                            }
 | 
			
		||||
                            return null;
 | 
			
		||||
                        }
 | 
			
		||||
                    }.exec();
 | 
			
		||||
                    break;
 | 
			
		||||
                case "su_access":
 | 
			
		||||
                    getApplication().suAccessState = Utils.getPrefsInt(prefs, "su_access", 0);
 | 
			
		||||
                    Shell.su("setprop persist.sys.root_access " + getApplication().suAccessState);
 | 
			
		||||
                    break;
 | 
			
		||||
                case "su_request_timeout":
 | 
			
		||||
                    getApplication().suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10);
 | 
			
		||||
                    break;
 | 
			
		||||
                case "su_auto_response":
 | 
			
		||||
                    getApplication().suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0);
 | 
			
		||||
                    break;
 | 
			
		||||
                case "su_notification":
 | 
			
		||||
                    getApplication().suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1);
 | 
			
		||||
                    break;
 | 
			
		||||
                case "developer_logging":
 | 
			
		||||
                    MagiskManager.devLogging = prefs.getBoolean("developer_logging", false);
 | 
			
		||||
                    break;
 | 
			
		||||
                case "shell_logging":
 | 
			
		||||
                    MagiskManager.shellLogging = prefs.getBoolean("shell_logging", false);
 | 
			
		||||
                    break;
 | 
			
		||||
            }
 | 
			
		||||
            setSummary();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void setSummary() {
 | 
			
		||||
            suAccess.setSummary(getResources()
 | 
			
		||||
                    .getStringArray(R.array.su_access)[getApplication().suAccessState]);
 | 
			
		||||
            autoRes.setSummary(getResources()
 | 
			
		||||
                    .getStringArray(R.array.auto_response)[getApplication().suResponseType]);
 | 
			
		||||
            suNotification.setSummary(getResources()
 | 
			
		||||
                    .getStringArray(R.array.su_notification)[getApplication().suNotificationType]);
 | 
			
		||||
            requestTimeout.setSummary(
 | 
			
		||||
                    getString(R.string.request_timeout_summary, prefs.getString("su_request_timeout", "10")));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,73 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk;
 | 
			
		||||
 | 
			
		||||
import android.app.job.JobInfo;
 | 
			
		||||
import android.app.job.JobScheduler;
 | 
			
		||||
import android.content.ComponentName;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.os.Bundle;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.GetBootBlocks;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.LoadApps;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.LoadModules;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.LoadRepos;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.MagiskHide;
 | 
			
		||||
import com.topjohnwu.magisk.components.Activity;
 | 
			
		||||
import com.topjohnwu.magisk.services.UpdateCheckService;
 | 
			
		||||
 | 
			
		||||
public class SplashActivity extends Activity{
 | 
			
		||||
 | 
			
		||||
    private static final int UPDATE_SERVICE_ID = 1;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
			
		||||
 | 
			
		||||
        super.onCreate(savedInstanceState);
 | 
			
		||||
 | 
			
		||||
        MagiskManager magiskManager = getApplicationContext();
 | 
			
		||||
 | 
			
		||||
        // Init the info and configs and root shell
 | 
			
		||||
        magiskManager.init();
 | 
			
		||||
 | 
			
		||||
        // Initialize the update check service, notify every 3 hours
 | 
			
		||||
        if (!"install".equals(getIntent().getStringExtra(MagiskManager.INTENT_SECTION))) {
 | 
			
		||||
            ComponentName service = new ComponentName(magiskManager, UpdateCheckService.class);
 | 
			
		||||
            JobInfo jobInfo = new JobInfo.Builder(UPDATE_SERVICE_ID, service)
 | 
			
		||||
                    .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
 | 
			
		||||
                    .setPersisted(true)
 | 
			
		||||
                    .setPeriodic(3 * 60 * 60 * 1000)
 | 
			
		||||
                    .build();
 | 
			
		||||
            JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
 | 
			
		||||
            scheduler.schedule(jobInfo);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Now fire all async tasks
 | 
			
		||||
        new GetBootBlocks(this).exec();
 | 
			
		||||
        if (magiskManager.magiskHide && magiskManager.magiskVersion > 11 &&
 | 
			
		||||
                !magiskManager.magiskHideStarted) {
 | 
			
		||||
            new MagiskHide().enable();
 | 
			
		||||
        }
 | 
			
		||||
        new LoadModules(this) {
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void onPostExecute(Void v) {
 | 
			
		||||
                super.onPostExecute(v);
 | 
			
		||||
                new LoadRepos(activity).exec();
 | 
			
		||||
            }
 | 
			
		||||
        }.exec();
 | 
			
		||||
        new LoadApps(this).exec();
 | 
			
		||||
        new CheckUpdates(this, false){
 | 
			
		||||
            @Override
 | 
			
		||||
            protected void onPostExecute(Void v) {
 | 
			
		||||
                super.onPostExecute(v);
 | 
			
		||||
                String section = getIntent().getStringExtra(MagiskManager.INTENT_SECTION);
 | 
			
		||||
                Intent intent = new Intent(magiskManager, MainActivity.class);
 | 
			
		||||
                if (section != null) {
 | 
			
		||||
                    intent.putExtra(MagiskManager.INTENT_SECTION, section);
 | 
			
		||||
                }
 | 
			
		||||
                startActivity(intent);
 | 
			
		||||
                finish();
 | 
			
		||||
            }
 | 
			
		||||
        }.exec();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@@ -1,223 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.adapters;
 | 
			
		||||
 | 
			
		||||
import android.animation.Animator;
 | 
			
		||||
import android.animation.ValueAnimator;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
import android.support.design.widget.Snackbar;
 | 
			
		||||
import android.support.v7.widget.RecyclerView;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.view.ViewTreeObserver;
 | 
			
		||||
import android.widget.ImageView;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.Switch;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
 | 
			
		||||
import com.topjohnwu.magisk.components.SnackbarMaker;
 | 
			
		||||
import com.topjohnwu.magisk.database.SuDatabaseHelper;
 | 
			
		||||
import com.topjohnwu.magisk.superuser.Policy;
 | 
			
		||||
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
 | 
			
		||||
public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> {
 | 
			
		||||
 | 
			
		||||
    private List<Policy> policyList;
 | 
			
		||||
    private SuDatabaseHelper dbHelper;
 | 
			
		||||
    private PackageManager pm;
 | 
			
		||||
    private Set<Policy> expandList = new HashSet<>();
 | 
			
		||||
 | 
			
		||||
    public PolicyAdapter(List<Policy> list, SuDatabaseHelper db, PackageManager pm) {
 | 
			
		||||
        policyList = list;
 | 
			
		||||
        dbHelper = db;
 | 
			
		||||
        this.pm = pm;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 | 
			
		||||
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_policy, parent, false);
 | 
			
		||||
        return new ViewHolder(v);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onBindViewHolder(ViewHolder holder, int position) {
 | 
			
		||||
        Policy policy = policyList.get(position);
 | 
			
		||||
        try {
 | 
			
		||||
            holder.setExpanded(expandList.contains(policy));
 | 
			
		||||
 | 
			
		||||
            holder.itemView.setOnClickListener(view -> {
 | 
			
		||||
                if (holder.mExpanded) {
 | 
			
		||||
                    holder.collapse();
 | 
			
		||||
                    expandList.remove(policy);
 | 
			
		||||
                } else {
 | 
			
		||||
                    holder.expand();
 | 
			
		||||
                    expandList.add(policy);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            holder.appName.setText(policy.appName);
 | 
			
		||||
            holder.packageName.setText(policy.packageName);
 | 
			
		||||
            holder.appIcon.setImageDrawable(pm.getPackageInfo(policy.packageName, 0).applicationInfo.loadIcon(pm));
 | 
			
		||||
            holder.masterSwitch.setOnCheckedChangeListener((v, isChecked) -> {
 | 
			
		||||
                if ((isChecked && policy.policy == Policy.DENY) ||
 | 
			
		||||
                        (!isChecked && policy.policy == Policy.ALLOW)) {
 | 
			
		||||
                    policy.policy = isChecked ? Policy.ALLOW : Policy.DENY;
 | 
			
		||||
                    String message = v.getContext().getString(
 | 
			
		||||
                            isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName);
 | 
			
		||||
                    SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
 | 
			
		||||
                    dbHelper.addPolicy(policy);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            holder.notificationSwitch.setOnCheckedChangeListener((v, isChecked) -> {
 | 
			
		||||
                if ((isChecked && !policy.notification) ||
 | 
			
		||||
                        (!isChecked && policy.notification)) {
 | 
			
		||||
                    policy.notification = isChecked;
 | 
			
		||||
                    String message = v.getContext().getString(
 | 
			
		||||
                            isChecked ? R.string.su_snack_notif_on : R.string.su_snack_notif_off, policy.appName);
 | 
			
		||||
                    SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
 | 
			
		||||
                    dbHelper.addPolicy(policy);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            holder.loggingSwitch.setOnCheckedChangeListener((v, isChecked) -> {
 | 
			
		||||
                if ((isChecked && !policy.logging) ||
 | 
			
		||||
                        (!isChecked && policy.logging)) {
 | 
			
		||||
                    policy.logging = isChecked;
 | 
			
		||||
                    String message = v.getContext().getString(
 | 
			
		||||
                            isChecked ? R.string.su_snack_log_on : R.string.su_snack_log_off, policy.appName);
 | 
			
		||||
                    SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
 | 
			
		||||
                    dbHelper.addPolicy(policy);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            holder.delete.setOnClickListener(v -> new AlertDialogBuilder(v.getContext())
 | 
			
		||||
                    .setTitle(R.string.su_revoke_title)
 | 
			
		||||
                    .setMessage(v.getContext().getString(R.string.su_revoke_msg, policy.appName))
 | 
			
		||||
                    .setPositiveButton(R.string.yes, (dialog, which) -> {
 | 
			
		||||
                        policyList.remove(position);
 | 
			
		||||
                        notifyItemRemoved(position);
 | 
			
		||||
                        notifyItemRangeChanged(position, policyList.size());
 | 
			
		||||
                        SnackbarMaker.make(holder.itemView, v.getContext().getString(R.string.su_snack_revoke, policy.appName),
 | 
			
		||||
                                Snackbar.LENGTH_SHORT).show();
 | 
			
		||||
                        dbHelper.deletePolicy(policy.uid);
 | 
			
		||||
                    })
 | 
			
		||||
                    .setNegativeButton(R.string.no_thanks, null)
 | 
			
		||||
                    .setCancelable(true)
 | 
			
		||||
                    .show());
 | 
			
		||||
            holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW);
 | 
			
		||||
            holder.notificationSwitch.setChecked(policy.notification);
 | 
			
		||||
            holder.loggingSwitch.setChecked(policy.logging);
 | 
			
		||||
 | 
			
		||||
            // Hide for now
 | 
			
		||||
            holder.moreInfo.setVisibility(View.GONE);
 | 
			
		||||
 | 
			
		||||
        } catch (PackageManager.NameNotFoundException e) {
 | 
			
		||||
            policyList.remove(position);
 | 
			
		||||
            dbHelper.deletePolicy(policy.uid);
 | 
			
		||||
            onBindViewHolder(holder, position);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getItemCount() {
 | 
			
		||||
        return policyList.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static class ViewHolder extends RecyclerView.ViewHolder {
 | 
			
		||||
 | 
			
		||||
        @BindView(R.id.app_name) TextView appName;
 | 
			
		||||
        @BindView(R.id.package_name) TextView packageName;
 | 
			
		||||
        @BindView(R.id.expand_layout) LinearLayout expandLayout;
 | 
			
		||||
        @BindView(R.id.app_icon) ImageView appIcon;
 | 
			
		||||
        @BindView(R.id.master_switch) Switch masterSwitch;
 | 
			
		||||
        @BindView(R.id.notification_switch) Switch notificationSwitch;
 | 
			
		||||
        @BindView(R.id.logging_switch) Switch loggingSwitch;
 | 
			
		||||
 | 
			
		||||
        @BindView(R.id.delete) ImageView delete;
 | 
			
		||||
        @BindView(R.id.more_info) ImageView moreInfo;
 | 
			
		||||
 | 
			
		||||
        private ValueAnimator mAnimator;
 | 
			
		||||
        private boolean mExpanded = false;
 | 
			
		||||
        private static int expandHeight = 0;
 | 
			
		||||
 | 
			
		||||
        ViewHolder(View itemView) {
 | 
			
		||||
            super(itemView);
 | 
			
		||||
            ButterKnife.bind(this, itemView);
 | 
			
		||||
            expandLayout.getViewTreeObserver().addOnPreDrawListener(
 | 
			
		||||
                    new ViewTreeObserver.OnPreDrawListener() {
 | 
			
		||||
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public boolean onPreDraw() {
 | 
			
		||||
                            if (expandHeight == 0) {
 | 
			
		||||
                                final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
 | 
			
		||||
                                final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
 | 
			
		||||
                                expandLayout.measure(widthSpec, heightSpec);
 | 
			
		||||
                                expandHeight = expandLayout.getMeasuredHeight();
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            expandLayout.getViewTreeObserver().removeOnPreDrawListener(this);
 | 
			
		||||
                            expandLayout.setVisibility(View.GONE);
 | 
			
		||||
                            mAnimator = slideAnimator(0, expandHeight);
 | 
			
		||||
                            return true;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void setExpanded(boolean expanded) {
 | 
			
		||||
            mExpanded = expanded;
 | 
			
		||||
            ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
 | 
			
		||||
            layoutParams.height = expanded ? expandHeight : 0;
 | 
			
		||||
            expandLayout.setLayoutParams(layoutParams);
 | 
			
		||||
            expandLayout.setVisibility(expanded ? View.VISIBLE : View.GONE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void expand() {
 | 
			
		||||
            expandLayout.setVisibility(View.VISIBLE);
 | 
			
		||||
            mAnimator.start();
 | 
			
		||||
            mExpanded = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void collapse() {
 | 
			
		||||
            if (!mExpanded) return;
 | 
			
		||||
            int finalHeight = expandLayout.getHeight();
 | 
			
		||||
            ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
 | 
			
		||||
            mAnimator.addListener(new Animator.AnimatorListener() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onAnimationEnd(Animator animator) {
 | 
			
		||||
                    expandLayout.setVisibility(View.GONE);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onAnimationStart(Animator animator) {}
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onAnimationCancel(Animator animator) {}
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onAnimationRepeat(Animator animator) {}
 | 
			
		||||
            });
 | 
			
		||||
            mAnimator.start();
 | 
			
		||||
            mExpanded = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private ValueAnimator slideAnimator(int start, int end) {
 | 
			
		||||
 | 
			
		||||
            ValueAnimator animator = ValueAnimator.ofInt(start, end);
 | 
			
		||||
 | 
			
		||||
            animator.addUpdateListener(valueAnimator -> {
 | 
			
		||||
                int value = (Integer) valueAnimator.getAnimatedValue();
 | 
			
		||||
                ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
 | 
			
		||||
                layoutParams.height = value;
 | 
			
		||||
                expandLayout.setLayoutParams(layoutParams);
 | 
			
		||||
            });
 | 
			
		||||
            return animator;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,122 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.adapters;
 | 
			
		||||
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.support.v7.widget.RecyclerView;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.ImageView;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.asyncs.ProcessRepoZip;
 | 
			
		||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
 | 
			
		||||
import com.topjohnwu.magisk.components.MarkDownWindow;
 | 
			
		||||
import com.topjohnwu.magisk.module.Repo;
 | 
			
		||||
import com.topjohnwu.magisk.receivers.DownloadReceiver;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
 | 
			
		||||
public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder> {
 | 
			
		||||
 | 
			
		||||
    private List<Repo> mUpdateRepos, mInstalledRepos, mOthersRepos;
 | 
			
		||||
    private Context mContext;
 | 
			
		||||
 | 
			
		||||
    public ReposAdapter(List<Repo> update, List<Repo> installed, List<Repo> others) {
 | 
			
		||||
        mUpdateRepos = update;
 | 
			
		||||
        mInstalledRepos = installed;
 | 
			
		||||
        mOthersRepos = others;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
 | 
			
		||||
        mContext = parent.getContext();
 | 
			
		||||
        View v = LayoutInflater.from(mContext).inflate(R.layout.list_item_repo, parent, false);
 | 
			
		||||
        return new ViewHolder(v);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onBindViewHolder(final ViewHolder holder, int position) {
 | 
			
		||||
        Repo repo = getItem(position);
 | 
			
		||||
 | 
			
		||||
        holder.title.setText(repo.getName());
 | 
			
		||||
        holder.versionName.setText(repo.getVersion());
 | 
			
		||||
        String author = repo.getAuthor();
 | 
			
		||||
        holder.author.setText(TextUtils.isEmpty(author) ? null : mContext.getString(R.string.author, author));
 | 
			
		||||
        holder.description.setText(repo.getDescription());
 | 
			
		||||
 | 
			
		||||
        holder.infoLayout.setOnClickListener(v -> new MarkDownWindow(null, repo.getDetailUrl(), mContext));
 | 
			
		||||
 | 
			
		||||
        holder.downloadImage.setOnClickListener(v -> {
 | 
			
		||||
            String filename = repo.getName() + "-" + repo.getVersion() + ".zip";
 | 
			
		||||
            new AlertDialogBuilder(mContext)
 | 
			
		||||
                    .setTitle(mContext.getString(R.string.repo_install_title, repo.getName()))
 | 
			
		||||
                    .setMessage(mContext.getString(R.string.repo_install_msg, filename))
 | 
			
		||||
                    .setCancelable(true)
 | 
			
		||||
                    .setPositiveButton(R.string.install, (d, i) -> Utils.dlAndReceive(
 | 
			
		||||
                            mContext,
 | 
			
		||||
                            new DownloadReceiver() {
 | 
			
		||||
                                @Override
 | 
			
		||||
                                public void onDownloadDone(Uri uri) {
 | 
			
		||||
                                    new ProcessRepoZip(activity, uri, true).exec();
 | 
			
		||||
                                }
 | 
			
		||||
                            },
 | 
			
		||||
                            repo.getZipUrl(),
 | 
			
		||||
                            Utils.getLegalFilename(filename)))
 | 
			
		||||
                    .setNeutralButton(R.string.download, (d, i) -> Utils.dlAndReceive(
 | 
			
		||||
                            mContext,
 | 
			
		||||
                            new DownloadReceiver() {
 | 
			
		||||
                                @Override
 | 
			
		||||
                                public void onDownloadDone(Uri uri) {
 | 
			
		||||
                                    new ProcessRepoZip(activity, uri, false).exec();
 | 
			
		||||
                                }
 | 
			
		||||
                            },
 | 
			
		||||
                            repo.getZipUrl(),
 | 
			
		||||
                            Utils.getLegalFilename(filename)))
 | 
			
		||||
                    .setNegativeButton(R.string.no_thanks, null)
 | 
			
		||||
                    .show();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getItemCount() {
 | 
			
		||||
        return mUpdateRepos.size() + mInstalledRepos.size() + mOthersRepos.size();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private Repo getItem(int position) {
 | 
			
		||||
        if (position >= mUpdateRepos.size()) {
 | 
			
		||||
            position -= mUpdateRepos.size();
 | 
			
		||||
            if (position >= mInstalledRepos.size()) {
 | 
			
		||||
                position -= mInstalledRepos.size();
 | 
			
		||||
                return mOthersRepos.get(position);
 | 
			
		||||
            } else {
 | 
			
		||||
                return mInstalledRepos.get(position);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return mUpdateRepos.get(position);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static class ViewHolder extends RecyclerView.ViewHolder {
 | 
			
		||||
 | 
			
		||||
        @BindView(R.id.title) TextView title;
 | 
			
		||||
        @BindView(R.id.version_name) TextView versionName;
 | 
			
		||||
        @BindView(R.id.description) TextView description;
 | 
			
		||||
        @BindView(R.id.author) TextView author;
 | 
			
		||||
        @BindView(R.id.info_layout) LinearLayout infoLayout;
 | 
			
		||||
        @BindView(R.id.download) ImageView downloadImage;
 | 
			
		||||
 | 
			
		||||
        ViewHolder(View itemView) {
 | 
			
		||||
            super(itemView);
 | 
			
		||||
            ButterKnife.bind(this, itemView);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,178 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.adapters;
 | 
			
		||||
 | 
			
		||||
import android.support.v7.widget.RecyclerView;
 | 
			
		||||
import android.util.SparseArray;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Comparator;
 | 
			
		||||
 | 
			
		||||
public class SimpleSectionedRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
 | 
			
		||||
 | 
			
		||||
    private static final int SECTION_TYPE = 0;
 | 
			
		||||
 | 
			
		||||
    private boolean mValid = true;
 | 
			
		||||
    private int mSectionResourceId;
 | 
			
		||||
    private int mTextResourceId;
 | 
			
		||||
    private RecyclerView.Adapter mBaseAdapter;
 | 
			
		||||
    private SparseArray<Section> mSections = new SparseArray<Section>();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public SimpleSectionedRecyclerViewAdapter(int sectionResourceId, int textResourceId,
 | 
			
		||||
                                              RecyclerView.Adapter baseAdapter) {
 | 
			
		||||
 | 
			
		||||
        mSectionResourceId = sectionResourceId;
 | 
			
		||||
        mTextResourceId = textResourceId;
 | 
			
		||||
        mBaseAdapter = baseAdapter;
 | 
			
		||||
 | 
			
		||||
        mBaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onChanged() {
 | 
			
		||||
                mValid = mBaseAdapter.getItemCount()>0;
 | 
			
		||||
                notifyDataSetChanged();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onItemRangeChanged(int positionStart, int itemCount) {
 | 
			
		||||
                mValid = mBaseAdapter.getItemCount()>0;
 | 
			
		||||
                notifyItemRangeChanged(positionStart, itemCount);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onItemRangeInserted(int positionStart, int itemCount) {
 | 
			
		||||
                mValid = mBaseAdapter.getItemCount()>0;
 | 
			
		||||
                notifyItemRangeInserted(positionStart, itemCount);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            @Override
 | 
			
		||||
            public void onItemRangeRemoved(int positionStart, int itemCount) {
 | 
			
		||||
                mValid = mBaseAdapter.getItemCount()>0;
 | 
			
		||||
                notifyItemRangeRemoved(positionStart, itemCount);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static class SectionViewHolder extends RecyclerView.ViewHolder {
 | 
			
		||||
 | 
			
		||||
        public TextView title;
 | 
			
		||||
 | 
			
		||||
        public SectionViewHolder(View view, int mTextResourceid) {
 | 
			
		||||
            super(view);
 | 
			
		||||
            title = (TextView) view.findViewById(mTextResourceid);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int typeView) {
 | 
			
		||||
        if (typeView == SECTION_TYPE) {
 | 
			
		||||
            View view = LayoutInflater.from(parent.getContext()).inflate(mSectionResourceId, parent, false);
 | 
			
		||||
            return new SectionViewHolder(view,mTextResourceId);
 | 
			
		||||
        }else{
 | 
			
		||||
            return mBaseAdapter.onCreateViewHolder(parent, typeView -1);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void onBindViewHolder(RecyclerView.ViewHolder sectionViewHolder, int position) {
 | 
			
		||||
        if (isSectionHeaderPosition(position)) {
 | 
			
		||||
            ((SectionViewHolder)sectionViewHolder).title.setText(mSections.get(position).title);
 | 
			
		||||
        }else{
 | 
			
		||||
            mBaseAdapter.onBindViewHolder(sectionViewHolder,sectionedPositionToPosition(position));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getItemViewType(int position) {
 | 
			
		||||
        return isSectionHeaderPosition(position)
 | 
			
		||||
                ? SECTION_TYPE
 | 
			
		||||
                : mBaseAdapter.getItemViewType(sectionedPositionToPosition(position)) +1 ;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public static class Section {
 | 
			
		||||
        int firstPosition;
 | 
			
		||||
        int sectionedPosition;
 | 
			
		||||
        CharSequence title;
 | 
			
		||||
 | 
			
		||||
        public Section(int firstPosition, CharSequence title) {
 | 
			
		||||
            this.firstPosition = firstPosition;
 | 
			
		||||
            this.title = title;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        public CharSequence getTitle() {
 | 
			
		||||
            return title;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    public void setSections(Section[] sections) {
 | 
			
		||||
        mSections.clear();
 | 
			
		||||
 | 
			
		||||
        Arrays.sort(sections, new Comparator<Section>() {
 | 
			
		||||
            @Override
 | 
			
		||||
            public int compare(Section o, Section o1) {
 | 
			
		||||
                return (o.firstPosition == o1.firstPosition)
 | 
			
		||||
                        ? 0
 | 
			
		||||
                        : ((o.firstPosition < o1.firstPosition) ? -1 : 1);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        int offset = 0; // offset positions for the headers we're adding
 | 
			
		||||
        for (Section section : sections) {
 | 
			
		||||
            section.sectionedPosition = section.firstPosition + offset;
 | 
			
		||||
            mSections.append(section.sectionedPosition, section);
 | 
			
		||||
            ++offset;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        notifyDataSetChanged();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int positionToSectionedPosition(int position) {
 | 
			
		||||
        int offset = 0;
 | 
			
		||||
        for (int i = 0; i < mSections.size(); i++) {
 | 
			
		||||
            if (mSections.valueAt(i).firstPosition > position) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            ++offset;
 | 
			
		||||
        }
 | 
			
		||||
        return position + offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public int sectionedPositionToPosition(int sectionedPosition) {
 | 
			
		||||
        if (isSectionHeaderPosition(sectionedPosition)) {
 | 
			
		||||
            return RecyclerView.NO_POSITION;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        int offset = 0;
 | 
			
		||||
        for (int i = 0; i < mSections.size(); i++) {
 | 
			
		||||
            if (mSections.valueAt(i).sectionedPosition > sectionedPosition) {
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            --offset;
 | 
			
		||||
        }
 | 
			
		||||
        return sectionedPosition + offset;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public boolean isSectionHeaderPosition(int position) {
 | 
			
		||||
        return mSections.get(position) != null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public long getItemId(int position) {
 | 
			
		||||
        return isSectionHeaderPosition(position)
 | 
			
		||||
                ? Integer.MAX_VALUE - mSections.indexOfKey(position)
 | 
			
		||||
                : mBaseAdapter.getItemId(sectionedPositionToPosition(position));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public int getItemCount() {
 | 
			
		||||
        return (mValid ? mBaseAdapter.getItemCount() + mSections.size() : 0);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,242 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.adapters;
 | 
			
		||||
 | 
			
		||||
import android.animation.Animator;
 | 
			
		||||
import android.animation.ValueAnimator;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.support.v7.widget.RecyclerView;
 | 
			
		||||
import android.view.LayoutInflater;
 | 
			
		||||
import android.view.View;
 | 
			
		||||
import android.view.ViewGroup;
 | 
			
		||||
import android.view.ViewTreeObserver;
 | 
			
		||||
import android.view.animation.Animation;
 | 
			
		||||
import android.view.animation.RotateAnimation;
 | 
			
		||||
import android.widget.ImageView;
 | 
			
		||||
import android.widget.LinearLayout;
 | 
			
		||||
import android.widget.TextView;
 | 
			
		||||
 | 
			
		||||
import com.thoughtbot.expandablerecyclerview.ExpandableRecyclerViewAdapter;
 | 
			
		||||
import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
 | 
			
		||||
import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder;
 | 
			
		||||
import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder;
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.superuser.SuLogEntry;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.HashSet;
 | 
			
		||||
import java.util.LinkedHashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
import butterknife.BindView;
 | 
			
		||||
import butterknife.ButterKnife;
 | 
			
		||||
 | 
			
		||||
public class SuLogAdapter {
 | 
			
		||||
 | 
			
		||||
    private ExpandableAdapter adapter;
 | 
			
		||||
    private Set<SuLogEntry> expandList = new HashSet<>();
 | 
			
		||||
 | 
			
		||||
    public SuLogAdapter(List<SuLogEntry> list) {
 | 
			
		||||
 | 
			
		||||
        // Separate the logs with date
 | 
			
		||||
        Map<String, List<SuLogEntry>> logEntryMap = new LinkedHashMap<>();
 | 
			
		||||
        List<SuLogEntry> group;
 | 
			
		||||
        for (SuLogEntry log : list) {
 | 
			
		||||
            String date = log.getDateString();
 | 
			
		||||
            group = logEntryMap.get(date);
 | 
			
		||||
            if (group == null) {
 | 
			
		||||
                group = new ArrayList<>();
 | 
			
		||||
                logEntryMap.put(date, group);
 | 
			
		||||
            }
 | 
			
		||||
            group.add(log);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Then format them into expandable groups
 | 
			
		||||
        List<LogGroup> logEntryGroups = new ArrayList<>();
 | 
			
		||||
        for (Map.Entry<String, List<SuLogEntry>> entry : logEntryMap.entrySet()) {
 | 
			
		||||
            logEntryGroups.add(new LogGroup(entry.getKey(), entry.getValue()));
 | 
			
		||||
        }
 | 
			
		||||
        adapter = new ExpandableAdapter(logEntryGroups);
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public RecyclerView.Adapter getAdapter() {
 | 
			
		||||
        return adapter;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class ExpandableAdapter
 | 
			
		||||
            extends ExpandableRecyclerViewAdapter<LogGroupViewHolder, LogViewHolder> {
 | 
			
		||||
 | 
			
		||||
        ExpandableAdapter(List<? extends ExpandableGroup> groups) {
 | 
			
		||||
            super(groups);
 | 
			
		||||
            expandableList.expandedGroupIndexes[0] = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public LogGroupViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
 | 
			
		||||
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog_group, parent, false);
 | 
			
		||||
            return new LogGroupViewHolder(v);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public LogViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
 | 
			
		||||
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog, parent, false);
 | 
			
		||||
            return new LogViewHolder(v);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onBindChildViewHolder(LogViewHolder holder, int flatPosition, ExpandableGroup group, int childIndex) {
 | 
			
		||||
            Context context = holder.itemView.getContext();
 | 
			
		||||
            SuLogEntry logEntry = (SuLogEntry) group.getItems().get(childIndex);
 | 
			
		||||
            holder.setExpanded(expandList.contains(logEntry));
 | 
			
		||||
            holder.itemView.setOnClickListener(view -> {
 | 
			
		||||
                if (holder.mExpanded) {
 | 
			
		||||
                    holder.collapse();
 | 
			
		||||
                    expandList.remove(logEntry);
 | 
			
		||||
                } else {
 | 
			
		||||
                    holder.expand();
 | 
			
		||||
                    expandList.add(logEntry);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
            holder.appName.setText(logEntry.appName);
 | 
			
		||||
            holder.action.setText(context.getString(logEntry.action ? R.string.grant : R.string.deny));
 | 
			
		||||
            holder.command.setText(logEntry.command);
 | 
			
		||||
            holder.fromPid.setText(String.valueOf(logEntry.fromPid));
 | 
			
		||||
            holder.toUid.setText(String.valueOf(logEntry.toUid));
 | 
			
		||||
            holder.time.setText(logEntry.getTimeString());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void onBindGroupViewHolder(LogGroupViewHolder holder, int flatPosition, ExpandableGroup group) {
 | 
			
		||||
            holder.date.setText(group.getTitle());
 | 
			
		||||
            if (isGroupExpanded(flatPosition)) {
 | 
			
		||||
                holder.expand();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private class LogGroup extends ExpandableGroup<SuLogEntry> {
 | 
			
		||||
        LogGroup(String title, List<SuLogEntry> items) {
 | 
			
		||||
            super(title, items);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static class LogGroupViewHolder extends GroupViewHolder {
 | 
			
		||||
 | 
			
		||||
        @BindView(R.id.date) TextView date;
 | 
			
		||||
        @BindView(R.id.arrow) ImageView arrow;
 | 
			
		||||
 | 
			
		||||
        public LogGroupViewHolder(View itemView) {
 | 
			
		||||
            super(itemView);
 | 
			
		||||
            ButterKnife.bind(this, itemView);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void expand() {
 | 
			
		||||
            RotateAnimation rotate =
 | 
			
		||||
                    new RotateAnimation(360, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
 | 
			
		||||
            rotate.setDuration(300);
 | 
			
		||||
            rotate.setFillAfter(true);
 | 
			
		||||
            arrow.setAnimation(rotate);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        @Override
 | 
			
		||||
        public void collapse() {
 | 
			
		||||
            RotateAnimation rotate =
 | 
			
		||||
                    new RotateAnimation(180, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
 | 
			
		||||
            rotate.setDuration(300);
 | 
			
		||||
            rotate.setFillAfter(true);
 | 
			
		||||
            arrow.setAnimation(rotate);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    static class LogViewHolder extends ChildViewHolder {
 | 
			
		||||
 | 
			
		||||
        @BindView(R.id.app_name) TextView appName;
 | 
			
		||||
        @BindView(R.id.action) TextView action;
 | 
			
		||||
        @BindView(R.id.time) TextView time;
 | 
			
		||||
        @BindView(R.id.fromPid) TextView fromPid;
 | 
			
		||||
        @BindView(R.id.toUid) TextView toUid;
 | 
			
		||||
        @BindView(R.id.command) TextView command;
 | 
			
		||||
        @BindView(R.id.expand_layout) LinearLayout expandLayout;
 | 
			
		||||
 | 
			
		||||
        private ValueAnimator mAnimator;
 | 
			
		||||
        private boolean mExpanded = false;
 | 
			
		||||
        private static int expandHeight = 0;
 | 
			
		||||
 | 
			
		||||
        public LogViewHolder(View itemView) {
 | 
			
		||||
            super(itemView);
 | 
			
		||||
            ButterKnife.bind(this, itemView);
 | 
			
		||||
            expandLayout.getViewTreeObserver().addOnPreDrawListener(
 | 
			
		||||
                    new ViewTreeObserver.OnPreDrawListener() {
 | 
			
		||||
 | 
			
		||||
                        @Override
 | 
			
		||||
                        public boolean onPreDraw() {
 | 
			
		||||
                            if (expandHeight == 0) {
 | 
			
		||||
                                final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
 | 
			
		||||
                                final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
 | 
			
		||||
                                expandLayout.measure(widthSpec, heightSpec);
 | 
			
		||||
                                expandHeight = expandLayout.getMeasuredHeight();
 | 
			
		||||
                            }
 | 
			
		||||
 | 
			
		||||
                            expandLayout.getViewTreeObserver().removeOnPreDrawListener(this);
 | 
			
		||||
                            expandLayout.setVisibility(View.GONE);
 | 
			
		||||
                            mAnimator = slideAnimator(0, expandHeight);
 | 
			
		||||
                            return true;
 | 
			
		||||
                        }
 | 
			
		||||
 | 
			
		||||
                    });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void setExpanded(boolean expanded) {
 | 
			
		||||
            mExpanded = expanded;
 | 
			
		||||
            ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
 | 
			
		||||
            layoutParams.height = expanded ? expandHeight : 0;
 | 
			
		||||
            expandLayout.setLayoutParams(layoutParams);
 | 
			
		||||
            expandLayout.setVisibility(expanded ? View.VISIBLE : View.GONE);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void expand() {
 | 
			
		||||
            expandLayout.setVisibility(View.VISIBLE);
 | 
			
		||||
            mAnimator.start();
 | 
			
		||||
            mExpanded = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private void collapse() {
 | 
			
		||||
            if (!mExpanded) return;
 | 
			
		||||
            int finalHeight = expandLayout.getHeight();
 | 
			
		||||
            ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
 | 
			
		||||
            mAnimator.addListener(new Animator.AnimatorListener() {
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onAnimationEnd(Animator animator) {
 | 
			
		||||
                    expandLayout.setVisibility(View.GONE);
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onAnimationStart(Animator animator) {}
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onAnimationCancel(Animator animator) {}
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                public void onAnimationRepeat(Animator animator) {}
 | 
			
		||||
            });
 | 
			
		||||
            mAnimator.start();
 | 
			
		||||
            mExpanded = false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        private ValueAnimator slideAnimator(int start, int end) {
 | 
			
		||||
 | 
			
		||||
            ValueAnimator animator = ValueAnimator.ofInt(start, end);
 | 
			
		||||
 | 
			
		||||
            animator.addUpdateListener(valueAnimator -> {
 | 
			
		||||
                int value = (Integer) valueAnimator.getAnimatedValue();
 | 
			
		||||
                ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
 | 
			
		||||
                layoutParams.height = value;
 | 
			
		||||
                expandLayout.setLayoutParams(layoutParams);
 | 
			
		||||
            });
 | 
			
		||||
            return animator;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,72 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.app.NotificationManager;
 | 
			
		||||
import android.app.PendingIntent;
 | 
			
		||||
import android.content.Context;
 | 
			
		||||
import android.content.Intent;
 | 
			
		||||
import android.support.v4.app.TaskStackBuilder;
 | 
			
		||||
import android.support.v7.app.NotificationCompat;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.SplashActivity;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.magisk.utils.WebService;
 | 
			
		||||
 | 
			
		||||
import org.json.JSONException;
 | 
			
		||||
import org.json.JSONObject;
 | 
			
		||||
 | 
			
		||||
public class CheckUpdates extends ParallelTask<Void, Void, Void> {
 | 
			
		||||
 | 
			
		||||
    private static final String UPDATE_JSON = "https://raw.githubusercontent.com/topjohnwu/MagiskManager/updates/magisk_update.json";
 | 
			
		||||
    private static final int NOTIFICATION_ID = 1;
 | 
			
		||||
 | 
			
		||||
    private boolean showNotification = false;
 | 
			
		||||
 | 
			
		||||
    public CheckUpdates(Context context, boolean b) {
 | 
			
		||||
        this(context);
 | 
			
		||||
        showNotification = b;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CheckUpdates(Context context) {
 | 
			
		||||
        magiskManager = Utils.getMagiskManager(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Void doInBackground(Void... voids) {
 | 
			
		||||
        String jsonStr = WebService.request(UPDATE_JSON, WebService.GET);
 | 
			
		||||
        try {
 | 
			
		||||
            JSONObject json = new JSONObject(jsonStr);
 | 
			
		||||
            JSONObject magisk = json.getJSONObject("magisk");
 | 
			
		||||
            magiskManager.remoteMagiskVersion = magisk.getDouble("versionCode");
 | 
			
		||||
            magiskManager.magiskLink = magisk.getString("link");
 | 
			
		||||
            magiskManager.releaseNoteLink = magisk.getString("note");
 | 
			
		||||
        } catch (JSONException ignored) {
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostExecute(Void v) {
 | 
			
		||||
        if (magiskManager.magiskVersion < magiskManager.remoteMagiskVersion
 | 
			
		||||
                && showNotification && magiskManager.updateNotification) {
 | 
			
		||||
            NotificationCompat.Builder builder = new NotificationCompat.Builder(magiskManager);
 | 
			
		||||
            builder.setSmallIcon(R.drawable.ic_magisk)
 | 
			
		||||
                    .setContentTitle(magiskManager.getString(R.string.magisk_update_title))
 | 
			
		||||
                    .setContentText(magiskManager.getString(R.string.magisk_update_available, magiskManager.remoteMagiskVersion))
 | 
			
		||||
                    .setVibrate(new long[]{0, 100, 100, 100})
 | 
			
		||||
                    .setAutoCancel(true);
 | 
			
		||||
            Intent intent = new Intent(magiskManager, SplashActivity.class);
 | 
			
		||||
            intent.putExtra(MagiskManager.INTENT_SECTION, "install");
 | 
			
		||||
            TaskStackBuilder stackBuilder = TaskStackBuilder.create(magiskManager);
 | 
			
		||||
            stackBuilder.addParentStack(SplashActivity.class);
 | 
			
		||||
            stackBuilder.addNextIntent(intent);
 | 
			
		||||
            PendingIntent pendingIntent = stackBuilder.getPendingIntent(NOTIFICATION_ID, PendingIntent.FLAG_UPDATE_CURRENT);
 | 
			
		||||
            builder.setContentIntent(pendingIntent);
 | 
			
		||||
            NotificationManager notificationManager =
 | 
			
		||||
                    (NotificationManager) magiskManager.getSystemService(Context.NOTIFICATION_SERVICE);
 | 
			
		||||
            notificationManager.notify(NOTIFICATION_ID, builder.build());
 | 
			
		||||
        }
 | 
			
		||||
        magiskManager.updateCheckDone.trigger();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,154 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.app.ProgressDialog;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Logger;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.magisk.utils.ZipUtils;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileNotFoundException;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.IOException;
 | 
			
		||||
import java.io.InputStream;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class FlashZip extends SerialTask<Void, String, Integer> {
 | 
			
		||||
 | 
			
		||||
    private Uri mUri;
 | 
			
		||||
    private File mCachedFile, mScriptFile, mCheckFile;
 | 
			
		||||
 | 
			
		||||
    private String mFilename;
 | 
			
		||||
    private ProgressDialog progress;
 | 
			
		||||
 | 
			
		||||
    public FlashZip(Activity context, Uri uri) {
 | 
			
		||||
        super(context);
 | 
			
		||||
        mUri = uri;
 | 
			
		||||
 | 
			
		||||
        mCachedFile = new File(magiskManager.getCacheDir(), "install.zip");
 | 
			
		||||
        mScriptFile = new File(magiskManager.getCacheDir(), "/META-INF/com/google/android/update-binary");
 | 
			
		||||
        mCheckFile = new File(mScriptFile.getParent(), "updater-script");
 | 
			
		||||
 | 
			
		||||
        // Try to get the filename ourselves
 | 
			
		||||
        mFilename = Utils.getNameFromUri(magiskManager, mUri);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void copyToCache() throws Throwable {
 | 
			
		||||
        publishProgress(magiskManager.getString(R.string.copying_msg));
 | 
			
		||||
 | 
			
		||||
        if (mCachedFile.exists() && !mCachedFile.delete()) {
 | 
			
		||||
            Logger.error("FlashZip: Error while deleting already existing file");
 | 
			
		||||
            throw new IOException();
 | 
			
		||||
        }
 | 
			
		||||
        try (
 | 
			
		||||
                InputStream in = magiskManager.getContentResolver().openInputStream(mUri);
 | 
			
		||||
                OutputStream outputStream = new FileOutputStream(mCachedFile)
 | 
			
		||||
        ) {
 | 
			
		||||
            byte buffer[] = new byte[1024];
 | 
			
		||||
            int length;
 | 
			
		||||
            if (in == null) throw new FileNotFoundException();
 | 
			
		||||
            while ((length = in.read(buffer)) > 0)
 | 
			
		||||
                outputStream.write(buffer, 0, length);
 | 
			
		||||
 | 
			
		||||
            Logger.dev("FlashZip: File created successfully - " + mCachedFile.getPath());
 | 
			
		||||
        } catch (FileNotFoundException e) {
 | 
			
		||||
            Logger.error("FlashZip: Invalid Uri");
 | 
			
		||||
            throw e;
 | 
			
		||||
        } catch (IOException e) {
 | 
			
		||||
            Logger.error("FlashZip: Error in creating file");
 | 
			
		||||
            throw e;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected boolean unzipAndCheck() throws Exception {
 | 
			
		||||
        ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android");
 | 
			
		||||
        List<String> ret;
 | 
			
		||||
        ret = Utils.readFile(mCheckFile.getPath());
 | 
			
		||||
        return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private int cleanup(int ret) {
 | 
			
		||||
        Shell.su(
 | 
			
		||||
                "rm -rf " + mCachedFile.getParent() + "/*",
 | 
			
		||||
                "rm -rf " + MagiskManager.TMP_FOLDER_PATH
 | 
			
		||||
        );
 | 
			
		||||
        return ret;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPreExecute() {
 | 
			
		||||
        progress = new ProgressDialog(activity);
 | 
			
		||||
        progress.setTitle(R.string.zip_install_progress_title);
 | 
			
		||||
        progress.show();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onProgressUpdate(String... values) {
 | 
			
		||||
        progress.setMessage(values[0]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Integer doInBackground(Void... voids) {
 | 
			
		||||
        Logger.dev("FlashZip Running... " + mFilename);
 | 
			
		||||
        List<String> ret;
 | 
			
		||||
        try {
 | 
			
		||||
            copyToCache();
 | 
			
		||||
            if (!unzipAndCheck()) return cleanup(0);
 | 
			
		||||
            publishProgress(magiskManager.getString(R.string.zip_install_progress_msg, mFilename));
 | 
			
		||||
            ret = Shell.su(
 | 
			
		||||
                    "BOOTMODE=true sh " + mScriptFile + " dummy 1 " + mCachedFile,
 | 
			
		||||
                    "if [ $? -eq 0 ]; then echo true; else echo false; fi"
 | 
			
		||||
            );
 | 
			
		||||
            if (!Utils.isValidShellResponse(ret)) return -1;
 | 
			
		||||
            Logger.dev("FlashZip: Console log:");
 | 
			
		||||
            for (String line : ret) {
 | 
			
		||||
                Logger.dev(line);
 | 
			
		||||
            }
 | 
			
		||||
            if (Boolean.parseBoolean(ret.get(ret.size() - 1)))
 | 
			
		||||
                return cleanup(1);
 | 
			
		||||
 | 
			
		||||
        } catch (Throwable e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
        }
 | 
			
		||||
        return cleanup(-1);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // -1 = error, manual install; 0 = invalid zip; 1 = success
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostExecute(Integer result) {
 | 
			
		||||
        super.onPostExecute(result);
 | 
			
		||||
        progress.dismiss();
 | 
			
		||||
        switch (result) {
 | 
			
		||||
            case -1:
 | 
			
		||||
                Toast.makeText(magiskManager, magiskManager.getString(R.string.install_error), Toast.LENGTH_LONG).show();
 | 
			
		||||
                Utils.showUriSnack(activity, mUri);
 | 
			
		||||
                break;
 | 
			
		||||
            case 0:
 | 
			
		||||
                Toast.makeText(magiskManager, magiskManager.getString(R.string.invalid_zip), Toast.LENGTH_LONG).show();
 | 
			
		||||
                break;
 | 
			
		||||
            case 1:
 | 
			
		||||
                onSuccess();
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    protected void onSuccess() {
 | 
			
		||||
        magiskManager.updateCheckDone.trigger();
 | 
			
		||||
        new LoadModules(activity).exec();
 | 
			
		||||
 | 
			
		||||
        new AlertDialogBuilder(activity)
 | 
			
		||||
                .setTitle(R.string.reboot_title)
 | 
			
		||||
                .setMessage(R.string.reboot_msg)
 | 
			
		||||
                .setPositiveButton(R.string.reboot, (dialogInterface, i) -> Shell.su(true, "reboot"))
 | 
			
		||||
                .setNegativeButton(R.string.no_thanks, null)
 | 
			
		||||
                .show();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,27 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
public class GetBootBlocks extends SerialTask<Void, Void, Void> {
 | 
			
		||||
 | 
			
		||||
    public GetBootBlocks(Activity context) {
 | 
			
		||||
        super(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Void doInBackground(Void... params) {
 | 
			
		||||
        magiskManager.blockList = Shell.su("ls /dev/block | grep mmc");
 | 
			
		||||
        if (magiskManager.bootBlock == null) {
 | 
			
		||||
            magiskManager.bootBlock = Utils.detectBootImage();
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostExecute(Void v) {
 | 
			
		||||
        magiskManager.blockDetectionDone.trigger();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,39 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.content.pm.ApplicationInfo;
 | 
			
		||||
import android.content.pm.PackageManager;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
 | 
			
		||||
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class LoadApps extends ParallelTask<Void, Void, Void> {
 | 
			
		||||
 | 
			
		||||
    public LoadApps(Activity context) {
 | 
			
		||||
        super(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Void doInBackground(Void... voids) {
 | 
			
		||||
        PackageManager pm = magiskManager.getPackageManager();
 | 
			
		||||
        List<ApplicationInfo> list = pm.getInstalledApplications(0);
 | 
			
		||||
        for (Iterator<ApplicationInfo> i = list.iterator(); i.hasNext(); ) {
 | 
			
		||||
            ApplicationInfo info = i.next();
 | 
			
		||||
            if (ApplicationAdapter.BLACKLIST.contains(info.packageName) || !info.enabled) {
 | 
			
		||||
                i.remove();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        Collections.sort(list, (a, b) -> a.loadLabel(pm).toString().toLowerCase()
 | 
			
		||||
                .compareTo(b.loadLabel(pm).toString().toLowerCase()));
 | 
			
		||||
        magiskManager.appList = Collections.unmodifiableList(list);
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostExecute(Void v) {
 | 
			
		||||
        new MagiskHide(activity).list();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,41 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.module.BaseModule;
 | 
			
		||||
import com.topjohnwu.magisk.module.Module;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Logger;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.magisk.utils.ValueSortedMap;
 | 
			
		||||
 | 
			
		||||
public class LoadModules extends SerialTask<Void, Void, Void> {
 | 
			
		||||
 | 
			
		||||
    public LoadModules(Activity context) {
 | 
			
		||||
        super(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Void doInBackground(Void... voids) {
 | 
			
		||||
        Logger.dev("LoadModules: Loading modules");
 | 
			
		||||
 | 
			
		||||
        magiskManager.moduleMap = new ValueSortedMap<>();
 | 
			
		||||
 | 
			
		||||
        for (String path : Utils.getModList(MagiskManager.MAGISK_PATH)) {
 | 
			
		||||
            Logger.dev("LoadModules: Adding modules from " + path);
 | 
			
		||||
            Module module;
 | 
			
		||||
            try {
 | 
			
		||||
                module = new Module(path);
 | 
			
		||||
                magiskManager.moduleMap.put(module.getId(), module);
 | 
			
		||||
            } catch (BaseModule.CacheModException ignored) {}
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        Logger.dev("LoadModules: Data load done");
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostExecute(Void v) {
 | 
			
		||||
        magiskManager.moduleLoadDone.trigger();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,188 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.content.SharedPreferences;
 | 
			
		||||
import android.text.TextUtils;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
 | 
			
		||||
import com.topjohnwu.magisk.module.BaseModule;
 | 
			
		||||
import com.topjohnwu.magisk.module.Repo;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Logger;
 | 
			
		||||
import com.topjohnwu.magisk.utils.ValueSortedMap;
 | 
			
		||||
import com.topjohnwu.magisk.utils.WebService;
 | 
			
		||||
 | 
			
		||||
import org.json.JSONArray;
 | 
			
		||||
import org.json.JSONObject;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.text.SimpleDateFormat;
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
import java.util.HashMap;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Locale;
 | 
			
		||||
import java.util.Map;
 | 
			
		||||
 | 
			
		||||
public class LoadRepos extends ParallelTask<Void, Void, Void> {
 | 
			
		||||
 | 
			
		||||
    public static final String ETAG_KEY = "ETag";
 | 
			
		||||
 | 
			
		||||
    private static final String REPO_URL = "https://api.github.com/users/Magisk-Modules-Repo/repos?per_page=100&page=%d";
 | 
			
		||||
    private static final String IF_NONE_MATCH = "If-None-Match";
 | 
			
		||||
    private static final String LINK_KEY = "Link";
 | 
			
		||||
 | 
			
		||||
    private static final int CHECK_ETAG = 0;
 | 
			
		||||
    private static final int LOAD_NEXT = 1;
 | 
			
		||||
    private static final int LOAD_PREV = 2;
 | 
			
		||||
 | 
			
		||||
    private List<String> etags;
 | 
			
		||||
    private ValueSortedMap<String, Repo> cached, fetched;
 | 
			
		||||
    private RepoDatabaseHelper repoDB;
 | 
			
		||||
    private SharedPreferences prefs;
 | 
			
		||||
 | 
			
		||||
    public LoadRepos(Activity context) {
 | 
			
		||||
        super(context);
 | 
			
		||||
        prefs = magiskManager.prefs;
 | 
			
		||||
        String prefsPath = context.getApplicationInfo().dataDir + "/shared_prefs";
 | 
			
		||||
        repoDB = new RepoDatabaseHelper(magiskManager);
 | 
			
		||||
        // Legacy data cleanup
 | 
			
		||||
        File old = new File(prefsPath, "RepoMap.xml");
 | 
			
		||||
        if (old.exists() || !prefs.getString("repomap", "empty").equals("empty")) {
 | 
			
		||||
            old.delete();
 | 
			
		||||
            prefs.edit().remove("version").remove("repomap").remove(ETAG_KEY).apply();
 | 
			
		||||
            repoDB.clearRepo();
 | 
			
		||||
        }
 | 
			
		||||
        etags = new ArrayList<>(
 | 
			
		||||
                Arrays.asList(magiskManager.prefs.getString(ETAG_KEY, "").split(",")));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private void loadJSON(String jsonString) throws Exception {
 | 
			
		||||
        JSONArray jsonArray = new JSONArray(jsonString);
 | 
			
		||||
 | 
			
		||||
        for (int i = 0; i < jsonArray.length(); i++) {
 | 
			
		||||
            JSONObject jsonobject = jsonArray.getJSONObject(i);
 | 
			
		||||
            String id = jsonobject.getString("description");
 | 
			
		||||
            String name = jsonobject.getString("name");
 | 
			
		||||
            String lastUpdate = jsonobject.getString("pushed_at");
 | 
			
		||||
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
 | 
			
		||||
            Date updatedDate = format.parse(lastUpdate);
 | 
			
		||||
            Repo repo = cached.get(id);
 | 
			
		||||
            try {
 | 
			
		||||
                if (repo == null) {
 | 
			
		||||
                    Logger.dev("LoadRepos: Create new repo " + id);
 | 
			
		||||
                    repo = new Repo(name, updatedDate);
 | 
			
		||||
                } else {
 | 
			
		||||
                    // Popout from cached
 | 
			
		||||
                    cached.remove(id);
 | 
			
		||||
                    repo.update(updatedDate);
 | 
			
		||||
                }
 | 
			
		||||
                if (repo.getId() != null) {
 | 
			
		||||
                    fetched.put(id, repo);
 | 
			
		||||
                }
 | 
			
		||||
            } catch (BaseModule.CacheModException ignored) {}
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean loadPage(int page, String url, int mode) {
 | 
			
		||||
        Logger.dev("LoadRepos: Loading page: " + (page + 1));
 | 
			
		||||
        Map<String, String> header = new HashMap<>();
 | 
			
		||||
        if (mode == CHECK_ETAG && page < etags.size() && !TextUtils.isEmpty(etags.get(page))) {
 | 
			
		||||
            Logger.dev("ETAG: " + etags.get(page));
 | 
			
		||||
            header.put(IF_NONE_MATCH, etags.get(page));
 | 
			
		||||
        }
 | 
			
		||||
        if (url == null) {
 | 
			
		||||
            url = String.format(Locale.US, REPO_URL, page + 1);
 | 
			
		||||
        }
 | 
			
		||||
        String jsonString = WebService.request(url, WebService.GET, header, true);
 | 
			
		||||
        if (TextUtils.isEmpty(jsonString)) {
 | 
			
		||||
            // At least check the pages we know
 | 
			
		||||
            return page + 1 < etags.size() && loadPage(page + 1, null, CHECK_ETAG);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // The request succeed, parse the new stuffs
 | 
			
		||||
        try {
 | 
			
		||||
            loadJSON(jsonString);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Update the ETAG
 | 
			
		||||
        String newEtag = header.get(ETAG_KEY);
 | 
			
		||||
        newEtag = newEtag.substring(newEtag.indexOf('\"'), newEtag.lastIndexOf('\"') + 1);
 | 
			
		||||
        Logger.dev("New ETAG: " + newEtag);
 | 
			
		||||
        if (page < etags.size()) {
 | 
			
		||||
            etags.set(page, newEtag);
 | 
			
		||||
        } else {
 | 
			
		||||
            etags.add(newEtag);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        String links = header.get(LINK_KEY);
 | 
			
		||||
        if (links != null) {
 | 
			
		||||
            if (mode == CHECK_ETAG || mode == LOAD_NEXT) {
 | 
			
		||||
                // Try to check next page URL
 | 
			
		||||
                url = null;
 | 
			
		||||
                for (String s : links.split(", ")) {
 | 
			
		||||
                    if (s.contains("next")) {
 | 
			
		||||
                        url = s.substring(s.indexOf("<") + 1, s.indexOf(">; "));
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (url != null) {
 | 
			
		||||
                    loadPage(page + 1, url, LOAD_NEXT);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (mode == CHECK_ETAG || mode == LOAD_PREV) {
 | 
			
		||||
                // Try to check prev page URL
 | 
			
		||||
                url = null;
 | 
			
		||||
                for (String s : links.split(", ")) {
 | 
			
		||||
                    if (s.contains("prev")) {
 | 
			
		||||
                        url = s.substring(s.indexOf("<") + 1, s.indexOf(">; "));
 | 
			
		||||
                        break;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                if (url != null) {
 | 
			
		||||
                    loadPage(page - 1, url, LOAD_PREV);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Void doInBackground(Void... voids) {
 | 
			
		||||
        Logger.dev("LoadRepos: Loading repos");
 | 
			
		||||
 | 
			
		||||
        cached = repoDB.getRepoMap(false);
 | 
			
		||||
        fetched = new ValueSortedMap<>();
 | 
			
		||||
 | 
			
		||||
        if (!loadPage(0, null, CHECK_ETAG)) {
 | 
			
		||||
            magiskManager.repoMap = repoDB.getRepoMap();
 | 
			
		||||
            Logger.dev("LoadRepos: No updates, use DB");
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        repoDB.addRepoMap(fetched);
 | 
			
		||||
        repoDB.removeRepo(cached);
 | 
			
		||||
 | 
			
		||||
        // Update ETag
 | 
			
		||||
        StringBuilder etagBuilder = new StringBuilder();
 | 
			
		||||
        for (int i = 0; i < etags.size(); ++i) {
 | 
			
		||||
            if (i != 0) etagBuilder.append(",");
 | 
			
		||||
            etagBuilder.append(etags.get(i));
 | 
			
		||||
        }
 | 
			
		||||
        prefs.edit().putString(ETAG_KEY, etagBuilder.toString()).apply();
 | 
			
		||||
 | 
			
		||||
        magiskManager.repoMap = repoDB.getRepoMap();
 | 
			
		||||
        Logger.dev("LoadRepos: Done");
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostExecute(Void v) {
 | 
			
		||||
        magiskManager.repoLoadDone.trigger();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,59 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
public class MagiskHide extends SerialTask<Object, Void, Void> {
 | 
			
		||||
 | 
			
		||||
    private boolean isList = false;
 | 
			
		||||
 | 
			
		||||
    public MagiskHide() {}
 | 
			
		||||
 | 
			
		||||
    public MagiskHide(Activity context) {
 | 
			
		||||
        super(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Void doInBackground(Object... params) {
 | 
			
		||||
        String command = (String) params[0];
 | 
			
		||||
        List<String> ret = Shell.su(MagiskManager.MAGISK_HIDE_PATH + command);
 | 
			
		||||
        if (isList) {
 | 
			
		||||
            magiskManager.magiskHideList = ret;
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostExecute(Void v) {
 | 
			
		||||
        if (isList) {
 | 
			
		||||
            magiskManager.magiskHideDone.trigger();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void add(CharSequence packageName) {
 | 
			
		||||
        exec("add " + packageName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void rm(CharSequence packageName) {
 | 
			
		||||
        exec("rm " + packageName);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void enable() {
 | 
			
		||||
        exec("enable; setprop persist.magisk.hide 1");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void disable() {
 | 
			
		||||
        exec("disable; setprop persist.magisk.hide 0");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public void list() {
 | 
			
		||||
        isList = true;
 | 
			
		||||
        if (magiskManager == null) return;
 | 
			
		||||
        exec("list");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@@ -1,24 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.os.AsyncTask;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
public abstract class ParallelTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
 | 
			
		||||
    protected Activity activity;
 | 
			
		||||
    protected MagiskManager magiskManager;
 | 
			
		||||
 | 
			
		||||
    public ParallelTask() {}
 | 
			
		||||
 | 
			
		||||
    public ParallelTask(Activity context) {
 | 
			
		||||
        activity = context;
 | 
			
		||||
        magiskManager = Utils.getMagiskManager(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SafeVarargs
 | 
			
		||||
    public final void exec(Params... params) {
 | 
			
		||||
        executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,96 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.app.ProgressDialog;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Logger;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.magisk.utils.ZipUtils;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
 | 
			
		||||
public class ProcessMagiskZip extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
 | 
			
		||||
    private Uri mUri;
 | 
			
		||||
    private ProgressDialog progressDialog;
 | 
			
		||||
    private String mBoot;
 | 
			
		||||
    private boolean mEnc, mVerity;
 | 
			
		||||
 | 
			
		||||
    public ProcessMagiskZip(Activity context, Uri uri, String boot, boolean enc, boolean verity) {
 | 
			
		||||
        super(context);
 | 
			
		||||
        mUri = uri;
 | 
			
		||||
        mBoot = boot;
 | 
			
		||||
        mEnc = enc;
 | 
			
		||||
        mVerity = verity;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPreExecute() {
 | 
			
		||||
        progressDialog = ProgressDialog.show(activity,
 | 
			
		||||
                activity.getString(R.string.zip_process_title),
 | 
			
		||||
                activity.getString(R.string.zip_unzip_msg));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Boolean doInBackground(Void... params) {
 | 
			
		||||
        if (Shell.rootAccess()) {
 | 
			
		||||
            try {
 | 
			
		||||
                // We might not have busybox yet, unzip with Java
 | 
			
		||||
                // We shall have complete busybox after Magisk installation
 | 
			
		||||
                File tempdir = new File(magiskManager.getCacheDir(), "magisk");
 | 
			
		||||
                ZipUtils.unzip(magiskManager.getContentResolver().openInputStream(mUri), tempdir);
 | 
			
		||||
                // Running in parallel mode, open new shell
 | 
			
		||||
                Shell.su(true,
 | 
			
		||||
                        "rm -f /dev/.magisk",
 | 
			
		||||
                        (mBoot != null) ? "echo \"BOOTIMAGE=/dev/block/" + mBoot + "\" >> /dev/.magisk" : "",
 | 
			
		||||
                        "echo \"KEEPFORCEENCRYPT=" + String.valueOf(mEnc) + "\" >> /dev/.magisk",
 | 
			
		||||
                        "echo \"KEEPVERITY=" + String.valueOf(mVerity) + "\" >> /dev/.magisk",
 | 
			
		||||
                        "mkdir -p " + MagiskManager.TMP_FOLDER_PATH,
 | 
			
		||||
                        "cp -af " + tempdir + "/. " + MagiskManager.TMP_FOLDER_PATH + "/magisk",
 | 
			
		||||
                        "mv -f " + tempdir + "/META-INF " + magiskManager.getCacheDir() + "/META-INF",
 | 
			
		||||
                        "rm -rf " + tempdir
 | 
			
		||||
                );
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                Logger.error("ProcessMagiskZip: Error!");
 | 
			
		||||
                e.printStackTrace();
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostExecute(Boolean result) {
 | 
			
		||||
        progressDialog.dismiss();
 | 
			
		||||
        if (result) {
 | 
			
		||||
            new FlashZip(activity, mUri) {
 | 
			
		||||
                @Override
 | 
			
		||||
                protected boolean unzipAndCheck() throws Exception {
 | 
			
		||||
                    // Don't need to check, as it is downloaded in correct form
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                @Override
 | 
			
		||||
                protected void onSuccess() {
 | 
			
		||||
                    new SerialTask<Void, Void, Void>(activity) {
 | 
			
		||||
                        @Override
 | 
			
		||||
                        protected Void doInBackground(Void... params) {
 | 
			
		||||
                            Shell.su("setprop magisk.version "
 | 
			
		||||
                                    + String.valueOf(magiskManager.remoteMagiskVersion));
 | 
			
		||||
                            magiskManager.updateCheckDone.trigger();
 | 
			
		||||
                            return null;
 | 
			
		||||
                        }
 | 
			
		||||
                    }.exec();
 | 
			
		||||
                    super.onSuccess();
 | 
			
		||||
                }
 | 
			
		||||
            }.exec();
 | 
			
		||||
        } else {
 | 
			
		||||
            Utils.showUriSnack(activity, mUri);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,116 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.app.ProgressDialog;
 | 
			
		||||
import android.net.Uri;
 | 
			
		||||
import android.widget.Toast;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.R;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Logger;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
import com.topjohnwu.magisk.utils.ZipUtils;
 | 
			
		||||
 | 
			
		||||
import java.io.File;
 | 
			
		||||
import java.io.FileInputStream;
 | 
			
		||||
import java.io.FileNotFoundException;
 | 
			
		||||
import java.io.FileOutputStream;
 | 
			
		||||
import java.io.OutputStream;
 | 
			
		||||
 | 
			
		||||
public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> {
 | 
			
		||||
 | 
			
		||||
    private Uri mUri;
 | 
			
		||||
    private ProgressDialog progressDialog;
 | 
			
		||||
    private boolean mInstall;
 | 
			
		||||
 | 
			
		||||
    public ProcessRepoZip(Activity context, Uri uri, boolean install) {
 | 
			
		||||
        super(context);
 | 
			
		||||
        mUri = uri;
 | 
			
		||||
        mInstall = install;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPreExecute() {
 | 
			
		||||
        progressDialog = ProgressDialog.show(activity,
 | 
			
		||||
                activity.getString(R.string.zip_process_title),
 | 
			
		||||
                activity.getString(R.string.zip_process_msg));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected Boolean doInBackground(Void... params) {
 | 
			
		||||
 | 
			
		||||
        FileInputStream in;
 | 
			
		||||
        FileOutputStream out;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
 | 
			
		||||
            // Create temp file
 | 
			
		||||
            File temp1 = new File(magiskManager.getCacheDir(), "1.zip");
 | 
			
		||||
            File temp2 = new File(magiskManager.getCacheDir(), "2.zip");
 | 
			
		||||
            if (magiskManager.getCacheDir().mkdirs()) {
 | 
			
		||||
                temp1.createNewFile();
 | 
			
		||||
                temp2.createNewFile();
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            out = new FileOutputStream(temp1);
 | 
			
		||||
 | 
			
		||||
            // First remove top folder in Github source zip, Uri -> temp1
 | 
			
		||||
            ZipUtils.removeTopFolder(activity.getContentResolver().openInputStream(mUri), out);
 | 
			
		||||
            out.flush();
 | 
			
		||||
            out.close();
 | 
			
		||||
 | 
			
		||||
            out = new FileOutputStream(temp2);
 | 
			
		||||
 | 
			
		||||
            // Then sign the zip for the first time, temp1 -> temp2
 | 
			
		||||
            ZipUtils.signZip(activity, temp1, out, false);
 | 
			
		||||
            out.flush();
 | 
			
		||||
            out.close();
 | 
			
		||||
 | 
			
		||||
            // Adjust the zip to prevent unzip issues, temp2 -> temp2
 | 
			
		||||
            ZipUtils.adjustZip(temp2);
 | 
			
		||||
 | 
			
		||||
            out = new FileOutputStream(temp1);
 | 
			
		||||
 | 
			
		||||
            // Finally, sign the whole zip file again, temp2 -> temp1
 | 
			
		||||
            ZipUtils.signZip(activity, temp2, out, true);
 | 
			
		||||
            out.flush();
 | 
			
		||||
            out.close();
 | 
			
		||||
 | 
			
		||||
            in = new FileInputStream(temp1);
 | 
			
		||||
 | 
			
		||||
            // Write it back to the downloaded zip, temp1 -> Uri
 | 
			
		||||
            try (OutputStream target = activity.getContentResolver().openOutputStream(mUri)) {
 | 
			
		||||
                byte[] buffer = new byte[4096];
 | 
			
		||||
                int length;
 | 
			
		||||
                if (target == null) throw  new FileNotFoundException();
 | 
			
		||||
                while ((length = in.read(buffer)) > 0)
 | 
			
		||||
                    target.write(buffer, 0, length);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // Delete the temp file
 | 
			
		||||
            temp1.delete();
 | 
			
		||||
            temp2.delete();
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            Logger.error("ProcessRepoZip: Error!");
 | 
			
		||||
            e.printStackTrace();
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected void onPostExecute(Boolean result) {
 | 
			
		||||
        progressDialog.dismiss();
 | 
			
		||||
        if (result) {
 | 
			
		||||
            if (Shell.rootAccess() && mInstall) {
 | 
			
		||||
                new FlashZip(activity, mUri).exec();
 | 
			
		||||
            } else {
 | 
			
		||||
                Utils.showUriSnack(activity, mUri);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        } else {
 | 
			
		||||
            Toast.makeText(activity, R.string.process_error, Toast.LENGTH_LONG).show();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,30 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.asyncs;
 | 
			
		||||
 | 
			
		||||
import android.app.Activity;
 | 
			
		||||
import android.os.AsyncTask;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Shell;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * This class is only used for running root commands
 | 
			
		||||
 **/
 | 
			
		||||
 | 
			
		||||
public abstract class SerialTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
 | 
			
		||||
    protected Activity activity;
 | 
			
		||||
    protected MagiskManager magiskManager;
 | 
			
		||||
 | 
			
		||||
    public SerialTask() {}
 | 
			
		||||
 | 
			
		||||
    public SerialTask(Activity context) {
 | 
			
		||||
        activity = context;
 | 
			
		||||
        magiskManager = Utils.getMagiskManager(context);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @SafeVarargs
 | 
			
		||||
    public final void exec(Params... params) {
 | 
			
		||||
        if (!Shell.rootAccess()) return;
 | 
			
		||||
        executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, params);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,13 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.components;
 | 
			
		||||
 | 
			
		||||
import android.support.v7.app.AppCompatActivity;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
 | 
			
		||||
public class Activity extends AppCompatActivity {
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public MagiskManager getApplicationContext() {
 | 
			
		||||
        return (MagiskManager) super.getApplicationContext();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +0,0 @@
 | 
			
		||||
package com.topjohnwu.magisk.components;
 | 
			
		||||
 | 
			
		||||
import com.topjohnwu.magisk.MagiskManager;
 | 
			
		||||
import com.topjohnwu.magisk.utils.Utils;
 | 
			
		||||
 | 
			
		||||
public class Fragment extends android.support.v4.app.Fragment {
 | 
			
		||||
 | 
			
		||||
    public MagiskManager getApplication() {
 | 
			
		||||
        return Utils.getMagiskManager(getActivity());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user