mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-10-31 10:40:52 +01:00 
			
		
		
		
	Compare commits
	
		
			470 Commits
		
	
	
		
			manager-v4
			...
			manager-v5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | c840a30c30 | ||
|   | ae5277a898 | ||
|   | bffa837825 | ||
|   | b9e7d0faea | ||
|   | 860b08d9ed | ||
|   | 691dc1d49e | ||
|   | 9d6886d367 | ||
|   | 9589b68f5a | ||
|   | 28d88af1af | ||
|   | 8b5acd1849 | ||
|   | 33dc63a7fd | ||
|   | d0a86385b7 | ||
|   | 50a49e2c8c | ||
|   | c60adb113e | ||
|   | aee015e8f6 | ||
|   | bf6af29205 | ||
|   | 329905d472 | ||
|   | 00d450d262 | ||
|   | 2365d1bd20 | ||
|   | 5b385c18e5 | ||
|   | 98c0434ec0 | ||
|   | f318d0a3bc | ||
|   | 27f5b410c0 | ||
|   | 3f55be9676 | ||
|   | b05d2d3a2d | ||
|   | 19af5f9e0b | ||
|   | f37f330670 | ||
|   | 40082d4571 | ||
|   | 00d655f346 | ||
|   | 821726e7c0 | ||
|   | 759e905c3c | ||
|   | 8bf7e42913 | ||
|   | 0dcd073554 | ||
|   | 2fe35d578d | ||
|   | 8d139e156e | ||
|   | 7c2849356a | ||
|   | 0025ffd1c0 | ||
|   | 2ef7146642 | ||
|   | 1b27e69e40 | ||
|   | 8e7b757efd | ||
|   | 1ab543cea1 | ||
|   | a3f86903e4 | ||
|   | c239c305ab | ||
|   | 2e02af994e | ||
|   | 836d9afe17 | ||
|   | 007a352742 | ||
|   | e526e5659e | ||
|   | 4a5227c7bf | ||
|   | c2c151ec4c | ||
|   | 452096e7e4 | ||
|   | 50c2a9859e | ||
|   | 677b667307 | ||
|   | 1adf331268 | ||
|   | 349b3e961b | ||
|   | 96650c06f0 | ||
|   | 26038a0a07 | ||
|   | 6a148b5dd9 | ||
|   | 0e109ef979 | ||
|   | 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