mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-10-31 10:40:52 +01:00 
			
		
		
		
	Compare commits
	
		
			340 Commits
		
	
	
		
			manager-v5
			...
			manager-v5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 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 | 
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -5,4 +5,8 @@ | ||||
| /build | ||||
| app/release | ||||
| *.hprof | ||||
| app/.externalNativeBuild/ | ||||
| .externalNativeBuild/ | ||||
| *.sh | ||||
| public.certificate.x509.pem | ||||
| private.key.pk8 | ||||
| *.apk | ||||
|   | ||||
| @@ -1,6 +1,2 @@ | ||||
| # Magisk Manager  | ||||
| You need to install CMake and NDK to build the zipadjust library for zip preprocessing | ||||
|  | ||||
| ## Pre-built Binaries | ||||
| Busybox (arm and x86) compiled by osm0sis (`libbusybox.so` under `app\src\main\jniLibs`)   | ||||
| Source and more info: [osm0sis' Odds and Ends](https://forum.xda-developers.com/showthread.php?t=2239421) | ||||
| # Magisk Manager | ||||
| 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,63 +0,0 @@ | ||||
| apply plugin: 'com.android.application' | ||||
|  | ||||
| android { | ||||
|     compileSdkVersion 26 | ||||
|     buildToolsVersion "26.0.0" | ||||
|  | ||||
|     defaultConfig { | ||||
|         applicationId "com.topjohnwu.magisk" | ||||
|         minSdkVersion 21 | ||||
|         targetSdkVersion 26 | ||||
|         versionCode 50 | ||||
|         versionName "5.1.0" | ||||
|         ndk { | ||||
|             moduleName 'zipadjust' | ||||
|             abiFilters 'x86', 'armeabi-v7a' | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     buildTypes { | ||||
|         release { | ||||
|             minifyEnabled true | ||||
|             shrinkResources true | ||||
|             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||||
|         } | ||||
|     } | ||||
|     compileOptions { | ||||
|         sourceCompatibility JavaVersion.VERSION_1_8 | ||||
|         targetCompatibility JavaVersion.VERSION_1_8 | ||||
|     } | ||||
|     dexOptions { | ||||
|         preDexLibraries true | ||||
|         javaMaxHeapSize "2g" | ||||
|     } | ||||
|     externalNativeBuild { | ||||
|         cmake { | ||||
|             path 'src/main/jni/CMakeLists.txt' | ||||
|         } | ||||
|     } | ||||
|     lintOptions { | ||||
|         disable 'MissingTranslation' | ||||
|     } | ||||
| } | ||||
| repositories { | ||||
|     jcenter() | ||||
|     maven { url "https://jitpack.io" } | ||||
|     maven { url "https://maven.google.com" } | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|     implementation fileTree(include: ['*.jar'], dir: 'libs') | ||||
|     implementation 'com.android.support:recyclerview-v7:26.0.0-beta2' | ||||
|     implementation 'com.android.support:cardview-v7:26.0.0-beta2' | ||||
|     implementation 'com.android.support:design:26.0.0-beta2' | ||||
|     implementation 'com.android.support:support-v4:26.0.0-beta2' | ||||
|     implementation 'com.jakewharton:butterknife:8.7.0' | ||||
|     implementation 'com.thoughtbot:expandablerecyclerview:1.4' | ||||
|     implementation 'us.feras.mdv:markdownview:1.1.0' | ||||
|     implementation 'com.madgag.spongycastle:core:1.54.0.0' | ||||
|     implementation 'com.madgag.spongycastle:prov:1.54.0.0' | ||||
|     implementation 'com.madgag.spongycastle:pkix:1.54.0.0' | ||||
|     implementation 'com.google.android.gms:play-services-safetynet:9.0.1' | ||||
|     annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0' | ||||
| } | ||||
| @@ -1,137 +0,0 @@ | ||||
| #!/system/bin/sh | ||||
| ########################################################################################## | ||||
| # | ||||
| # Magisk Uninstaller | ||||
| # by topjohnwu | ||||
| #  | ||||
| # This script can be placed in /cache/magisk_uninstaller.sh | ||||
| # The Magisk main binary will pick up the script, and uninstall itself, following a reboot | ||||
| # This script can also be used in flashable zip with the uninstaller_loader.sh | ||||
| #  | ||||
| # This script will try to do restoration with the following: | ||||
| # 1-1. Find and restore the original stock boot image dump (OTA proof) | ||||
| # 1-2. If 1-1 fails, restore ramdisk from the internal backup | ||||
| #      (ramdisk fully restored, not OTA friendly) | ||||
| # 1-3. If 1-2 fails, it will remove added files in ramdisk, however modified files | ||||
| #      are remained modified, because we have no backups. By doing so, Magisk will  | ||||
| #      not be started at boot, but this isn't actually 100% cleaned up | ||||
| # 2. Remove all Magisk related files | ||||
| #    (The list is LARGE, most likely due to bad decision in early versions | ||||
| #    the latest versions has much less bloat to cleanup) | ||||
| # | ||||
| ########################################################################################## | ||||
|  | ||||
| # Call ui_print_wrap if exists, or else simply use echo | ||||
| # Useful when wrapped in flashable zip | ||||
| ui_print_wrap() { | ||||
|   type ui_print >/dev/null 2>&1 && ui_print "$1" || echo "$1" | ||||
| } | ||||
|  | ||||
| # Call abort if exists, or else show error message and exit | ||||
| # Essential when wrapped in flashable zip | ||||
| abort_wrap() { | ||||
|   type abort >/dev/null 2>&1 | ||||
|   if [ $? -ne 0 ]; then | ||||
|     ui_print_wrap "$1" | ||||
|     exit 1 | ||||
|   else | ||||
|     abort "$1" | ||||
|   fi | ||||
| } | ||||
|  | ||||
| if [ ! -d $MAGISKBIN -o ! -f $MAGISKBIN/magiskboot -o ! -f $MAGISKBIN/util_functions.sh ]; then | ||||
|   ui_print_wrap "! Cannot find $MAGISKBIN" | ||||
|   exit 1 | ||||
| fi | ||||
|  | ||||
| [ -z $BOOTMODE ] && BOOTMODE=false | ||||
|  | ||||
| MAGISKBIN=/data/magisk | ||||
| CHROMEDIR=$MAGISKBIN/chromeos | ||||
|  | ||||
| # Default permissions | ||||
| umask 022 | ||||
|  | ||||
| # Load utility functions | ||||
| . $MAGISKBIN/util_functions.sh | ||||
|  | ||||
| # Find the boot image | ||||
| find_boot_image | ||||
| [ -z $BOOTIMAGE ] && abort "! Unable to detect boot image" | ||||
|  | ||||
| ui_print_wrap "- Found Boot Image: $BOOTIMAGE" | ||||
|  | ||||
| cd $MAGISKBIN | ||||
|  | ||||
| ui_print_wrap "- Unpacking boot image" | ||||
| ./magiskboot --unpack "$BOOTIMAGE" | ||||
| [ $? -ne 0 ] && abort_wrap "! Unable to unpack boot image" | ||||
|  | ||||
| # Update our previous backup to new format if exists | ||||
| if [ -f /data/stock_boot.img ]; then | ||||
|   SHA1=`./magiskboot --sha1 /data/stock_boot.img | tail -n 1` | ||||
|   STOCKDUMP=/data/stock_boot_${SHA1}.img | ||||
|   mv /data/stock_boot.img $STOCKDUMP | ||||
|   ./magiskboot --compress $STOCKDUMP | ||||
| fi | ||||
|  | ||||
| # Detect boot image state | ||||
| ./magiskboot --cpio-test ramdisk.cpio | ||||
| case $? in | ||||
|   0 )  # Stock boot | ||||
|     ui_print_wrap "- Stock boot image detected!" | ||||
|     ui_print_wrap "! Magisk is not installed!" | ||||
|     exit | ||||
|     ;; | ||||
|   1 )  # Magisk patched | ||||
|     ui_print_wrap "- Magisk patched image detected!" | ||||
|     # Find SHA1 of stock boot image | ||||
|     if [ -z $SHA1 ]; then | ||||
|       ./magiskboot --cpio-extract ramdisk.cpio init.magisk.rc init.magisk.rc.old | ||||
|       SHA1=`grep_prop "# STOCKSHA1" init.magisk.rc.old` | ||||
|       rm -f init.magisk.rc.old | ||||
|     fi | ||||
|     [ ! -z $SHA1 ] && STOCKDUMP=/data/stock_boot_${SHA1}.img | ||||
|     if [ -f ${STOCKDUMP}.gz ]; then | ||||
|       ui_print_wrap "- Boot image backup found!" | ||||
|       ./magiskboot --decompress ${STOCKDUMP}.gz stock_boot.img | ||||
|     else | ||||
|       ui_print_wrap "! Boot image backup unavailable" | ||||
|       ui_print_wrap "- Restoring ramdisk with backup" | ||||
|       ./magiskboot --cpio-restore ramdisk.cpio | ||||
|       ./magiskboot --repack $BOOTIMAGE stock_boot.img | ||||
|     fi | ||||
|     ;; | ||||
|   2 ) # Other patched | ||||
|     ui_print_wrap "! Boot image patched by other programs!" | ||||
|     abort_wrap "! Cannot uninstall with this uninstaller" | ||||
|     ;; | ||||
| esac | ||||
|  | ||||
| # Sign chromeos boot | ||||
| if [ -f chromeos ]; then | ||||
|   echo > empty | ||||
|  | ||||
|   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 empty --arch arm --bootloader empty --flags 0x1 | ||||
|  | ||||
|   rm -f empty stock_boot.img | ||||
|   mv stock_boot.img.signed stock_boot.img | ||||
| fi | ||||
|  | ||||
| ui_print_wrap "- Flashing stock/reverted image" | ||||
| if [ -L "$BOOTIMAGE" ]; then | ||||
|   dd if=stock_boot.img of="$BOOTIMAGE" bs=4096 | ||||
| else | ||||
|   cat stock_boot.img /dev/zero | dd of="$BOOTIMAGE" bs=4096 >/dev/null 2>&1 | ||||
| fi | ||||
| rm -f stock_boot.img | ||||
|  | ||||
| ui_print_wrap "- 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/magisk_debug.log \ | ||||
|         /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----- | ||||
| @@ -1,192 +0,0 @@ | ||||
| ########################################################################################## | ||||
| #  | ||||
| # Magisk General Utility Functions | ||||
| # by topjohnwu | ||||
| #  | ||||
| # Used in flash_script.sh, addon.d.sh, magisk module installers, and uninstaller | ||||
| #  | ||||
| ########################################################################################## | ||||
|  | ||||
| get_outfd() { | ||||
|   readlink /proc/$$/fd/$OUTFD 2>/dev/null | grep /tmp >/dev/null | ||||
|   if [ "$?" -eq "0" ]; then | ||||
|     OUTFD=0 | ||||
|  | ||||
|     for FD in `ls /proc/$$/fd`; do | ||||
|       readlink /proc/$$/fd/$FD 2>/dev/null | grep pipe >/dev/null | ||||
|       if [ "$?" -eq "0" ]; then | ||||
|         ps | grep " 3 $FD " | grep -v grep >/dev/null | ||||
|         if [ "$?" -eq "0" ]; then | ||||
|           OUTFD=$FD | ||||
|           break | ||||
|         fi | ||||
|       fi | ||||
|     done | ||||
|   fi | ||||
| } | ||||
|  | ||||
| ui_print() { | ||||
|   if $BOOTMODE; then | ||||
|     echo "$1" | ||||
|   else  | ||||
|     echo -n -e "ui_print $1\n" >> /proc/self/fd/$OUTFD | ||||
|     echo -n -e "ui_print\n" >> /proc/self/fd/$OUTFD | ||||
|   fi | ||||
| } | ||||
|  | ||||
| getvar() { | ||||
|   local VARNAME=$1 | ||||
|   local VALUE=$(eval echo \$"$VARNAME"); | ||||
|   for FILE in /dev/.magisk /data/.magisk /cache/.magisk /system/.magisk; do | ||||
|     if [ -z "$VALUE" ]; then | ||||
|       LINE=$(cat $FILE 2>/dev/null | grep "$VARNAME=") | ||||
|       if [ ! -z "$LINE" ]; then | ||||
|         VALUE=${LINE#*=} | ||||
|       fi | ||||
|     fi | ||||
|   done | ||||
|   eval $VARNAME=\$VALUE | ||||
| } | ||||
|  | ||||
| find_boot_image() { | ||||
|   if [ -z "$BOOTIMAGE" ]; then | ||||
|     for BLOCK in boot_a BOOT_A kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do | ||||
|       BOOTIMAGE=`ls /dev/block/by-name/$BLOCK || ls /dev/block/platform/*/by-name/$BLOCK || ls /dev/block/platform/*/*/by-name/$BLOCK` 2>/dev/null | ||||
|       [ ! -z $BOOTIMAGE ] && break | ||||
|     done | ||||
|   fi | ||||
|   # Recovery fallback | ||||
|   if [ -z "$BOOTIMAGE" ]; then | ||||
|     for FSTAB in /etc/*fstab*; do | ||||
|       BOOTIMAGE=`grep -E '\b/boot\b' $FSTAB | grep -v "#" | grep -oE '/dev/[a-zA-Z0-9_./-]*'` | ||||
|       [ ! -z $BOOTIMAGE ] && break | ||||
|     done | ||||
|   fi | ||||
|   [ -L "$BOOTIMAGE" ] && BOOTIMAGE=`readlink $BOOTIMAGE` | ||||
| } | ||||
|  | ||||
| is_mounted() { | ||||
|   if [ ! -z "$2" ]; then | ||||
|     cat /proc/mounts | grep $1 | grep $2, >/dev/null | ||||
|   else | ||||
|     cat /proc/mounts | grep $1 >/dev/null | ||||
|   fi | ||||
|   return $? | ||||
| } | ||||
|  | ||||
| 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 | ||||
| } | ||||
|  | ||||
| remove_system_su() { | ||||
|   if [ -f /system/bin/su -o -f /system/xbin/su ] && [ ! -f /su/bin/su ]; then | ||||
|     ui_print "! System installed root detected, mount rw :(" | ||||
|     mount -o rw,remount /system | ||||
|     # SuperSU | ||||
|     if [ -e /system/bin/.ext/.su ]; then | ||||
|       mv -f /system/bin/app_process32_original /system/bin/app_process32 2>/dev/null | ||||
|       mv -f /system/bin/app_process64_original /system/bin/app_process64 2>/dev/null | ||||
|       mv -f /system/bin/install-recovery_original.sh /system/bin/install-recovery.sh 2>/dev/null | ||||
|       cd /system/bin | ||||
|       if [ -e app_process64 ]; then | ||||
|         ln -sf app_process64 app_process | ||||
|       else | ||||
|         ln -sf app_process32 app_process | ||||
|       fi | ||||
|     fi | ||||
|     rm -rf /system/.pin /system/bin/.ext /system/etc/.installed_su_daemon /system/etc/.has_su_daemon \ | ||||
|     /system/xbin/daemonsu /system/xbin/su /system/xbin/sugote /system/xbin/sugote-mksh /system/xbin/supolicy \ | ||||
|     /system/bin/app_process_init /system/bin/su /cache/su /system/lib/libsupol.so /system/lib64/libsupol.so \ | ||||
|     /system/su.d /system/etc/install-recovery.sh /system/etc/init.d/99SuperSUDaemon /cache/install-recovery.sh \ | ||||
|     /system/.supersu /cache/.supersu /data/.supersu \ | ||||
|     /system/app/Superuser.apk /system/app/SuperSU /cache/Superuser.apk  2>/dev/null | ||||
|   fi | ||||
| } | ||||
|  | ||||
| api_level_arch_detect() { | ||||
|   API=`grep_prop ro.build.version.sdk` | ||||
|   ABI=`grep_prop ro.product.cpu.abi | cut -c-3` | ||||
|   ABI2=`grep_prop ro.product.cpu.abi2 | cut -c-3` | ||||
|   ABILONG=`grep_prop ro.product.cpu.abi` | ||||
|  | ||||
|   ARCH=arm | ||||
|   IS64BIT=false | ||||
|   if [ "$ABI" = "x86" ]; then ARCH=x86; fi; | ||||
|   if [ "$ABI2" = "x86" ]; then ARCH=x86; fi; | ||||
|   if [ "$ABILONG" = "arm64-v8a" ]; then ARCH=arm64; IS64BIT=true; fi; | ||||
|   if [ "$ABILONG" = "x86_64" ]; then ARCH=x64; IS64BIT=true; fi; | ||||
| } | ||||
|  | ||||
| recovery_actions() { | ||||
|   # TWRP bug fix | ||||
|   mount -o bind /dev/urandom /dev/random | ||||
|   # Temporarily block out all custom recovery binaries/libs | ||||
|   mv /sbin /sbin_tmp | ||||
|   # Add all possible library paths | ||||
|   OLD_LD_PATH=$LD_LIBRARY_PATH | ||||
|   $IS64BIT && export LD_LIBRARY_PATH=/system/lib64:/system/vendor/lib64 || export LD_LIBRARY_PATH=/system/lib:/system/vendor/lib | ||||
| } | ||||
|  | ||||
| recovery_cleanup() { | ||||
|   mv /sbin_tmp /sbin | ||||
|   # Clear LD_LIBRARY_PATH | ||||
|   export LD_LIBRARY_PATH=$OLD_LD_PATH | ||||
|   ui_print "- Unmounting partitions" | ||||
|   umount -l /system | ||||
|   umount -l /vendor 2>/dev/null | ||||
|   umount -l /dev/random | ||||
| } | ||||
|  | ||||
| abort() { | ||||
|   ui_print "$1" | ||||
|   mv /sbin_tmp /sbin 2>/dev/null | ||||
|   exit 1 | ||||
| } | ||||
|  | ||||
| set_perm() { | ||||
|   chown $2:$3 $1 || exit 1 | ||||
|   chmod $4 $1 || exit 1 | ||||
|   if [ ! -z $5 ]; then | ||||
|     chcon $5 $1 2>/dev/null | ||||
|   else | ||||
|     chcon 'u:object_r:system_file:s0' $1 2>/dev/null | ||||
|   fi | ||||
| } | ||||
|  | ||||
| set_perm_recursive() { | ||||
|   find $1 -type d 2>/dev/null | while read dir; do | ||||
|     set_perm $dir $2 $3 $4 $6 | ||||
|   done | ||||
|   find $1 -type f 2>/dev/null | while read file; do | ||||
|     set_perm $file $2 $3 $5 $6 | ||||
|   done | ||||
| } | ||||
|  | ||||
| mktouch() { | ||||
|   mkdir -p ${1%/*} | ||||
|   if [ -z "$2" ]; then | ||||
|     touch $1 | ||||
|   else | ||||
|     echo $2 > $1 | ||||
|   fi | ||||
|   chmod 644 $1 | ||||
| } | ||||
|  | ||||
| request_size_check() { | ||||
|   reqSizeM=`du -s $1 | cut -f1` | ||||
|   reqSizeM=$((reqSizeM / 1024 + 1)) | ||||
| } | ||||
|  | ||||
| image_size_check() { | ||||
|   SIZE="`$MAGISKBIN/magisk --imgsize $IMG`" | ||||
|   curUsedM=`echo "$SIZE" | cut -d" " -f1` | ||||
|   curSizeM=`echo "$SIZE" | cut -d" " -f2` | ||||
|   curFreeM=$((curSizeM - curUsedM)) | ||||
| } | ||||
|  | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 73 KiB | 
| @@ -1,133 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| 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.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.AboutCardRow; | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
| import com.topjohnwu.magisk.components.AlertDialogBuilder; | ||||
|  | ||||
| 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 = "https://www.paypal.me/topjohnwu"; | ||||
|     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); | ||||
|         if (getApplicationContext().isDarkTheme) { | ||||
|             setTheme(R.style.AppTheme_Transparent_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(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,98 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.support.v7.app.ActionBar; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.support.v7.widget.Toolbar; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.LinearLayout; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.asyncs.FlashZip; | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
| import com.topjohnwu.magisk.utils.AdaptiveList; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import butterknife.OnClick; | ||||
|  | ||||
| public class FlashActivity extends Activity { | ||||
|  | ||||
|     @BindView(R.id.toolbar) Toolbar toolbar; | ||||
|     @BindView(R.id.flash_logs) RecyclerView flashLogs; | ||||
|     @BindView(R.id.button_panel) LinearLayout buttonPanel; | ||||
|  | ||||
|     private AdaptiveList<String> rootShellOutput; | ||||
|  | ||||
|     @OnClick(R.id.no_thanks) | ||||
|     public void dismiss() { | ||||
|         finish(); | ||||
|     } | ||||
|  | ||||
|     @OnClick(R.id.reboot) | ||||
|     public void reboot() { | ||||
|         Shell.getShell(this).su_raw("reboot"); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setContentView(R.layout.activity_flash); | ||||
|         ButterKnife.bind(this); | ||||
|         rootShellOutput = new AdaptiveList<>(flashLogs); | ||||
|         setSupportActionBar(toolbar); | ||||
|         ActionBar ab = getSupportActionBar(); | ||||
|         if (ab != null) { | ||||
|             ab.setTitle(R.string.flashing); | ||||
|         } | ||||
|         setFloating(); | ||||
|  | ||||
|         flashLogs.setAdapter(new FlashLogAdapter()); | ||||
|  | ||||
|         // We must receive a Uri of the target zip | ||||
|         Uri uri = getIntent().getData(); | ||||
|  | ||||
|         new FlashZip(this, uri, rootShellOutput) | ||||
|                 .setCallBack(() -> buttonPanel.setVisibility(View.VISIBLE)) | ||||
|                 .exec(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBackPressed() { | ||||
|         // Prevent user accidentally press back button | ||||
|     } | ||||
|  | ||||
|     private class FlashLogAdapter extends RecyclerView.Adapter<ViewHolder> { | ||||
|  | ||||
|         @Override | ||||
|         public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | ||||
|             View view = LayoutInflater.from(parent.getContext()) | ||||
|                     .inflate(R.layout.list_item_flashlog, parent, false); | ||||
|             return new ViewHolder(view); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void onBindViewHolder(ViewHolder holder, int position) { | ||||
|             holder.text.setText(rootShellOutput.get(position)); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public int getItemCount() { | ||||
|             return rootShellOutput.size(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public class ViewHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         @BindView(R.id.textView) TextView text; | ||||
|  | ||||
|         public ViewHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             ButterKnife.bind(this, itemView); | ||||
|         } | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,233 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.app.Application; | ||||
| import android.app.NotificationChannel; | ||||
| import android.app.NotificationManager; | ||||
| import android.content.Context; | ||||
| import android.content.SharedPreferences; | ||||
| import android.content.pm.ApplicationInfo; | ||||
| import android.os.Build; | ||||
| import android.os.Handler; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.text.TextUtils; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import com.topjohnwu.magisk.database.SuDatabaseHelper; | ||||
| import com.topjohnwu.magisk.module.Module; | ||||
| import com.topjohnwu.magisk.module.Repo; | ||||
| import com.topjohnwu.magisk.utils.CallbackEvent; | ||||
| import com.topjohnwu.magisk.utils.SafetyNetHelper; | ||||
| 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 TMP_FOLDER_PATH = "/dev/tmp"; | ||||
|     public static final String MAGISK_PATH = "/magisk"; | ||||
|     public static final String UNINSTALLER = "magisk_uninstaller.sh"; | ||||
|     public static final String UTIL_FUNCTIONS= "util_functions.sh"; | ||||
|     public static final String INTENT_SECTION = "section"; | ||||
|     public static final String BUSYBOX_VERSION = "1.26.2"; | ||||
|     public static final String MAGISKHIDE_PROP = "persist.magisk.hide"; | ||||
|     public static final String DISABLE_INDICATION_PROP = "ro.magisk.disable"; | ||||
|     public static final String NOTIFICATION_CHANNEL = "magisk_update_notice"; | ||||
|  | ||||
|     // Events | ||||
|     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<>(); | ||||
|  | ||||
|     // Info | ||||
|     public String magiskVersionString; | ||||
|     public int magiskVersionCode = -1; | ||||
|     public String remoteMagiskVersionString; | ||||
|     public int remoteMagiskVersionCode = -1; | ||||
|     public String magiskLink; | ||||
|     public String releaseNoteLink; | ||||
|     public String remoteManagerVersionString; | ||||
|     public int remoteManagerVersionCode = -1; | ||||
|     public String managerLink; | ||||
|     public SafetyNetHelper.Result SNCheckResult; | ||||
|     public String bootBlock = null; | ||||
|     public boolean isSuClient = false; | ||||
|     public String suVersion = null; | ||||
|     public boolean disabled; | ||||
|  | ||||
|     // 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 suReauth; | ||||
|     public int suRequestTimeout; | ||||
|     public int suLogTimeout = 14; | ||||
|     public int suAccessState; | ||||
|     public int multiuserMode; | ||||
|     public int suResponseType; | ||||
|     public int suNotificationType; | ||||
|     public int suNamespaceMode; | ||||
|  | ||||
|     // Global resources | ||||
|     public SharedPreferences prefs; | ||||
|     public SuDatabaseHelper suDB; | ||||
|     public Shell shell; | ||||
|  | ||||
|     private static Handler mHandler = new Handler(); | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate() { | ||||
|         super.onCreate(); | ||||
|         new File(getApplicationInfo().dataDir).mkdirs();  /* Create the app data directory */ | ||||
|         prefs = PreferenceManager.getDefaultSharedPreferences(this); | ||||
|         shell = Shell.getShell(); | ||||
|     } | ||||
|  | ||||
|     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); | ||||
|         if (BuildConfig.DEBUG) { | ||||
|             devLogging = prefs.getBoolean("developer_logging", false); | ||||
|             shellLogging = prefs.getBoolean("shell_logging", false); | ||||
|         } else { | ||||
|             devLogging = false; | ||||
|             shellLogging = false; | ||||
|         } | ||||
|         magiskHide = prefs.getBoolean("magiskhide", true); | ||||
|         updateNotification = prefs.getBoolean("notification", true); | ||||
|         initSU(); | ||||
|         updateMagiskInfo(); | ||||
|         updateBlockInfo(); | ||||
|         // Initialize busybox | ||||
|         File busybox = new File(getApplicationInfo().dataDir + "/busybox/busybox"); | ||||
|         if (!busybox.exists() || !TextUtils.equals(prefs.getString("busybox_version", ""), BUSYBOX_VERSION)) { | ||||
|             busybox.getParentFile().mkdirs(); | ||||
|             shell.su_raw( | ||||
|                     "cp -f " + new File(getApplicationInfo().nativeLibraryDir, "libbusybox.so") + " " + busybox, | ||||
|                     "chmod -R 755 " + busybox.getParent(), | ||||
|                     busybox + " --install -s " + busybox.getParent() | ||||
|             ); | ||||
|         } | ||||
|         // Initialize prefs | ||||
|         prefs.edit() | ||||
|                 .putBoolean("dark_theme", isDarkTheme) | ||||
|                 .putBoolean("magiskhide", magiskHide) | ||||
|                 .putBoolean("notification", updateNotification) | ||||
|                 .putBoolean("hosts", new File("/magisk/.core/hosts").exists()) | ||||
|                 .putBoolean("disable", Utils.itemExist(shell, MAGISK_DISABLE_FILE)) | ||||
|                 .putBoolean("su_reauth", suReauth) | ||||
|                 .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)) | ||||
|                 .putString("multiuser_mode", String.valueOf(multiuserMode)) | ||||
|                 .putString("mnt_ns", String.valueOf(suNamespaceMode)) | ||||
|                 .putString("busybox_version", BUSYBOX_VERSION) | ||||
|                 .apply(); | ||||
|         // Add busybox to PATH | ||||
|         shell.su_raw("PATH=$PATH:" + busybox.getParent()); | ||||
|  | ||||
|         // Create notification channel on Android O | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|             NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL, | ||||
|                     getString(R.string.magisk_updates), NotificationManager.IMPORTANCE_DEFAULT); | ||||
|             ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     public void initSUConfig() { | ||||
|         suDB = new SuDatabaseHelper(this); | ||||
|         suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10); | ||||
|         suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0); | ||||
|         suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1); | ||||
|         suReauth = prefs.getBoolean("su_reauth", false); | ||||
|     } | ||||
|  | ||||
|     public void initSU() { | ||||
|         initSUConfig(); | ||||
|  | ||||
|         List<String> ret = shell.sh("su -v"); | ||||
|         if (Utils.isValidShellResponse(ret)) { | ||||
|             suVersion = ret.get(0); | ||||
|             isSuClient = suVersion.toUpperCase().contains("MAGISK"); | ||||
|         } | ||||
|         if (isSuClient) { | ||||
|             suAccessState = suDB.getSettings(SuDatabaseHelper.ROOT_ACCESS, 3); | ||||
|             multiuserMode = suDB.getSettings(SuDatabaseHelper.MULTIUSER_MODE, 0); | ||||
|             suNamespaceMode = suDB.getSettings(SuDatabaseHelper.MNT_NS, 1); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void updateMagiskInfo() { | ||||
|         List<String> ret; | ||||
|         ret = shell.sh("magisk -v"); | ||||
|         if (!Utils.isValidShellResponse(ret)) { | ||||
|             ret = shell.sh("getprop magisk.version"); | ||||
|             if (Utils.isValidShellResponse(ret)) { | ||||
|                 try { | ||||
|                     magiskVersionString = ret.get(0); | ||||
|                     magiskVersionCode = (int) Double.parseDouble(ret.get(0)) * 10; | ||||
|                 } catch (NumberFormatException ignored) {} | ||||
|             } | ||||
|         } else { | ||||
|             magiskVersionString = ret.get(0).split(":")[0]; | ||||
|             ret = shell.sh("magisk -V"); | ||||
|             try { | ||||
|                 magiskVersionCode = Integer.parseInt(ret.get(0)); | ||||
|             } catch (NumberFormatException ignored) {} | ||||
|         } | ||||
|         ret = shell.sh("getprop " + DISABLE_INDICATION_PROP); | ||||
|         try { | ||||
|             disabled = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0; | ||||
|         } catch (NumberFormatException e) { | ||||
|             disabled = false; | ||||
|         } | ||||
|         ret = shell.sh("getprop " + MAGISKHIDE_PROP); | ||||
|         try { | ||||
|             magiskHide = !Utils.isValidShellResponse(ret) || Integer.parseInt(ret.get(0)) != 0; | ||||
|         } catch (NumberFormatException e) { | ||||
|             magiskHide = true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void updateBlockInfo() { | ||||
|         List<String> res = shell.su( | ||||
|                 "for BLOCK in boot_a BOOT_A kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do", | ||||
|                 "BOOTIMAGE=`ls /dev/block/by-name/$BLOCK || ls /dev/block/platform/*/by-name/$BLOCK || ls /dev/block/platform/*/*/by-name/$BLOCK` 2>/dev/null", | ||||
|                 "[ ! -z \"$BOOTIMAGE\" ] && break", | ||||
|                 "done", | ||||
|                 "[ ! -z \"$BOOTIMAGE\" -a -L \"$BOOTIMAGE\" ] && BOOTIMAGE=`readlink $BOOTIMAGE`", | ||||
|                 "echo \"$BOOTIMAGE\"" | ||||
|         ); | ||||
|         if (Utils.isValidShellResponse(res)) { | ||||
|             bootBlock = res.get(0); | ||||
|         } else { | ||||
|             blockList = shell.su("ls -d /dev/block/mmc* /dev/block/sd* 2>/dev/null"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,234 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.content.SharedPreferences; | ||||
| import android.os.Build; | ||||
| 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.preference.SwitchPreference; | ||||
| import android.support.v7.app.ActionBar; | ||||
| import android.support.v7.widget.Toolbar; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import com.topjohnwu.magisk.asyncs.MagiskHide; | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
| import com.topjohnwu.magisk.components.AlertDialogBuilder; | ||||
| import com.topjohnwu.magisk.database.SuDatabaseHelper; | ||||
| 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_Transparent_Dark); | ||||
|         } | ||||
|  | ||||
|         setContentView(R.layout.activity_settings); | ||||
|         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 static class SettingsFragment extends PreferenceFragment | ||||
|             implements SharedPreferences.OnSharedPreferenceChangeListener { | ||||
|  | ||||
|         private SharedPreferences prefs; | ||||
|         private PreferenceScreen prefScreen; | ||||
|  | ||||
|         private ListPreference suAccess, autoRes, suNotification, requestTimeout, multiuserMode, namespaceMode; | ||||
|         private MagiskManager magiskManager; | ||||
|  | ||||
|         @Override | ||||
|         public void onCreate(Bundle savedInstanceState) { | ||||
|             super.onCreate(savedInstanceState); | ||||
|             addPreferencesFromResource(R.xml.app_settings); | ||||
|             prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); | ||||
|             prefScreen = getPreferenceScreen(); | ||||
|             magiskManager = Utils.getMagiskManager(getActivity()); | ||||
|  | ||||
|             PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk"); | ||||
|             PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser"); | ||||
|             PreferenceCategory developer = (PreferenceCategory) findPreference("developer"); | ||||
|  | ||||
|             suAccess = (ListPreference) findPreference("su_access"); | ||||
|             autoRes = (ListPreference) findPreference("su_auto_response"); | ||||
|             requestTimeout = (ListPreference) findPreference("su_request_timeout"); | ||||
|             suNotification = (ListPreference) findPreference("su_notification"); | ||||
|             multiuserMode = (ListPreference) findPreference("multiuser_mode"); | ||||
|             namespaceMode = (ListPreference) findPreference("mnt_ns"); | ||||
|             SwitchPreference reauth = (SwitchPreference) findPreference("su_reauth"); | ||||
|  | ||||
|             setSummary(); | ||||
|  | ||||
|             // Disable dangerous settings in user mode if selected owner manage | ||||
|             if (getActivity().getApplicationInfo().uid > 99999) { | ||||
|                 prefScreen.removePreference(magiskCategory); | ||||
|                 prefScreen.removePreference(suCategory); | ||||
|             } | ||||
|  | ||||
|             // Remove re-authentication option on Android O, it will not work | ||||
|             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|                 suCategory.removePreference(reauth); | ||||
|             } | ||||
|  | ||||
|             findPreference("clear").setOnPreferenceClickListener((pref) -> { | ||||
|                 Utils.clearRepoCache(getActivity()); | ||||
|                 return true; | ||||
|             }); | ||||
|  | ||||
|             if (!BuildConfig.DEBUG) { | ||||
|                 prefScreen.removePreference(developer); | ||||
|             } | ||||
|  | ||||
|             if (!Shell.rootAccess()) { | ||||
|                 prefScreen.removePreference(magiskCategory); | ||||
|                 prefScreen.removePreference(suCategory); | ||||
|             } else { | ||||
|                 if (!magiskManager.isSuClient) { | ||||
|                     prefScreen.removePreference(suCategory); | ||||
|                 } | ||||
|                 if (magiskManager.magiskVersionCode < 1300) { | ||||
|                     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 (magiskManager.isDarkTheme != enabled) { | ||||
|                         magiskManager.isDarkTheme = enabled; | ||||
|                         magiskManager.reloadMainActivity.trigger(); | ||||
|                         getActivity().recreate(); | ||||
|                     } | ||||
|                     break; | ||||
|                 case "disable": | ||||
|                     enabled = prefs.getBoolean("disable", false); | ||||
|                     if (enabled) { | ||||
|                         Utils.createFile(magiskManager.shell, MagiskManager.MAGISK_DISABLE_FILE); | ||||
|                     } else { | ||||
|                         Utils.removeItem(magiskManager.shell, MagiskManager.MAGISK_DISABLE_FILE); | ||||
|                     } | ||||
|                     Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show(); | ||||
|                     break; | ||||
|                 case "magiskhide": | ||||
|                     enabled = prefs.getBoolean("magiskhide", false); | ||||
|                     if (enabled) { | ||||
|                         if (!magiskManager.isSuClient) { | ||||
|                             new AlertDialogBuilder(getActivity()) | ||||
|                                     .setTitle(R.string.no_magisksu_title) | ||||
|                                     .setMessage(R.string.no_magisksu_msg) | ||||
|                                     .setPositiveButton(R.string.understand, (dialog, which) -> new MagiskHide(getActivity()).enable()) | ||||
|                                     .setCancelable(false) | ||||
|                                     .show(); | ||||
|                         } else { | ||||
|                             new MagiskHide(getActivity()).enable(); | ||||
|                         } | ||||
|                     } else { | ||||
|                         new MagiskHide(getActivity()).disable(); | ||||
|                     } | ||||
|                     break; | ||||
|                 case "hosts": | ||||
|                     enabled = prefs.getBoolean("hosts", false); | ||||
|                     if (enabled) { | ||||
|                         magiskManager.shell.su_raw( | ||||
|                                 "cp -af /system/etc/hosts /magisk/.core/hosts", | ||||
|                                 "mount -o bind /magisk/.core/hosts /system/etc/hosts"); | ||||
|                     } else { | ||||
|                         magiskManager.shell.su_raw( | ||||
|                                 "umount -l /system/etc/hosts", | ||||
|                                 "rm -f /magisk/.core/hosts"); | ||||
|                     } | ||||
|                     break; | ||||
|                 case "su_access": | ||||
|                     magiskManager.suAccessState = Utils.getPrefsInt(prefs, "su_access", 3); | ||||
|                     magiskManager.suDB.setSettings(SuDatabaseHelper.ROOT_ACCESS, magiskManager.suAccessState); | ||||
|                     break; | ||||
|                 case "multiuser_mode": | ||||
|                     magiskManager.multiuserMode = Utils.getPrefsInt(prefs, "multiuser_mode", 0); | ||||
|                     magiskManager.suDB.setSettings(SuDatabaseHelper.MULTIUSER_MODE, magiskManager.multiuserMode); | ||||
|                     break; | ||||
|                 case "mnt_ns": | ||||
|                     magiskManager.suNamespaceMode = Utils.getPrefsInt(prefs, "mnt_ns", 1); | ||||
|                     magiskManager.suDB.setSettings(SuDatabaseHelper.MNT_NS, magiskManager.suNamespaceMode); | ||||
|                     break; | ||||
|                 case "su_request_timeout": | ||||
|                     magiskManager.suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10); | ||||
|                     break; | ||||
|                 case "su_auto_response": | ||||
|                     magiskManager.suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0); | ||||
|                     break; | ||||
|                 case "su_notification": | ||||
|                     magiskManager.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)[magiskManager.suAccessState]); | ||||
|             autoRes.setSummary(getResources() | ||||
|                     .getStringArray(R.array.auto_response)[magiskManager.suResponseType]); | ||||
|             suNotification.setSummary(getResources() | ||||
|                     .getStringArray(R.array.su_notification)[magiskManager.suNotificationType]); | ||||
|             requestTimeout.setSummary( | ||||
|                     getString(R.string.request_timeout_summary, prefs.getString("su_request_timeout", "10"))); | ||||
|             multiuserMode.setSummary(getResources() | ||||
|                     .getStringArray(R.array.multiuser_summary)[magiskManager.multiuserMode]); | ||||
|             namespaceMode.setSummary(getResources() | ||||
|                     .getStringArray(R.array.namespace_summary)[magiskManager.suNamespaceMode]); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,58 +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 android.text.TextUtils; | ||||
|  | ||||
| import com.topjohnwu.magisk.asyncs.LoadApps; | ||||
| import com.topjohnwu.magisk.asyncs.LoadModules; | ||||
| import com.topjohnwu.magisk.asyncs.LoadRepos; | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
| import com.topjohnwu.magisk.services.UpdateCheckService; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| public class SplashActivity extends Activity{ | ||||
|  | ||||
|     private static final int UPDATE_SERVICE_ID = 1; | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|  | ||||
|         super.onCreate(savedInstanceState); | ||||
|  | ||||
|         // Init the info and configs and root sh | ||||
|         getApplicationContext().init(); | ||||
|  | ||||
|         // Now fire all async tasks | ||||
|         new LoadModules(this) | ||||
|                 .setCallBack(() -> new LoadRepos(this).exec()) | ||||
|                 .exec(); | ||||
|         new LoadApps(this).exec(); | ||||
|  | ||||
|         if (Utils.checkNetworkStatus(this)) { | ||||
|             // Initialize the update check service, notify every 8 hours | ||||
|             if (!TextUtils.equals("install", getIntent().getStringExtra(MagiskManager.INTENT_SECTION))) { | ||||
|                 ComponentName service = new ComponentName(this, UpdateCheckService.class); | ||||
|                 JobInfo jobInfo = new JobInfo.Builder(UPDATE_SERVICE_ID, service) | ||||
|                         .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY) | ||||
|                         .setPersisted(true) | ||||
|                         .setPeriodic(8 * 60 * 60 * 1000) | ||||
|                         .build(); | ||||
|                 JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE); | ||||
|                 scheduler.schedule(jobInfo); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Intent intent = new Intent(this, MainActivity.class); | ||||
|         String section = getIntent().getStringExtra(MagiskManager.INTENT_SECTION); | ||||
|         if (section != null) { | ||||
|             intent.putExtra(MagiskManager.INTENT_SECTION, section); | ||||
|         } | ||||
|         startActivity(intent); | ||||
|         finish(); | ||||
|     } | ||||
| } | ||||
| @@ -1,211 +0,0 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| import android.app.Activity; | ||||
| 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.Module; | ||||
| import com.topjohnwu.magisk.module.Repo; | ||||
| import com.topjohnwu.magisk.receivers.DownloadReceiver; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| import com.topjohnwu.magisk.utils.ValueSortedMap; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
|  | ||||
| public class ReposAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { | ||||
|  | ||||
|     private static final int SECTION_TYPE = 0; | ||||
|     private static final int REPO_TYPE = 1; | ||||
|  | ||||
|     private List<Repo> mUpdateRepos, mInstalledRepos, mOthersRepos; | ||||
|     private int[] sectionList; | ||||
|     private int size; | ||||
|     private ValueSortedMap<String, Repo> repoMap; | ||||
|  | ||||
|     public ReposAdapter(ValueSortedMap<String, Repo> map) { | ||||
|         repoMap = map; | ||||
|         mUpdateRepos = new ArrayList<>(); | ||||
|         mInstalledRepos = new ArrayList<>(); | ||||
|         mOthersRepos = new ArrayList<>(); | ||||
|         sectionList = new int[3]; | ||||
|         size = 0; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | ||||
|         Context context = parent.getContext(); | ||||
|         View v; | ||||
|         RecyclerView.ViewHolder holder = null; | ||||
|         switch (viewType) { | ||||
|             case SECTION_TYPE: | ||||
|                 v = LayoutInflater.from(context).inflate(R.layout.section, parent, false); | ||||
|                 holder = new SectionHolder(v); | ||||
|                 break; | ||||
|             case REPO_TYPE: | ||||
|                 v = LayoutInflater.from(context).inflate(R.layout.list_item_repo, parent, false); | ||||
|                 holder = new RepoHolder(v); | ||||
|                 break; | ||||
|         } | ||||
|         return holder; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { | ||||
|         Context context = holder.itemView.getContext(); | ||||
|         switch (getItemViewType(position)) { | ||||
|             case SECTION_TYPE: | ||||
|                 SectionHolder section = (SectionHolder) holder; | ||||
|                 if (position == sectionList[0]) { | ||||
|                     section.sectionText.setText(context.getString(R.string.update_available)); | ||||
|                 } else if (position == sectionList[1]) { | ||||
|                     section.sectionText.setText(context.getString(R.string.installed)); | ||||
|                 } else { | ||||
|                     section.sectionText.setText(context.getString(R.string.not_installed)); | ||||
|                 } | ||||
|                 break; | ||||
|             case REPO_TYPE: | ||||
|                 RepoHolder repoHolder = (RepoHolder) holder; | ||||
|                 Repo repo = getRepo(position); | ||||
|                 repoHolder.title.setText(repo.getName()); | ||||
|                 repoHolder.versionName.setText(repo.getVersion()); | ||||
|                 String author = repo.getAuthor(); | ||||
|                 repoHolder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author)); | ||||
|                 repoHolder.description.setText(repo.getDescription()); | ||||
|  | ||||
|                 repoHolder.infoLayout.setOnClickListener(v -> new MarkDownWindow(null, repo.getDetailUrl(), context)); | ||||
|  | ||||
|                 repoHolder.downloadImage.setOnClickListener(v -> { | ||||
|                     String filename = repo.getName() + "-" + repo.getVersion() + ".zip"; | ||||
|                     new AlertDialogBuilder(context) | ||||
|                             .setTitle(context.getString(R.string.repo_install_title, repo.getName())) | ||||
|                             .setMessage(context.getString(R.string.repo_install_msg, filename)) | ||||
|                             .setCancelable(true) | ||||
|                             .setPositiveButton(R.string.install, (d, i) -> Utils.dlAndReceive( | ||||
|                                     context, | ||||
|                                     new DownloadReceiver() { | ||||
|                                         @Override | ||||
|                                         public void onDownloadDone(Uri uri) { | ||||
|                                             new ProcessRepoZip((Activity) context, uri, true).exec(); | ||||
|                                         } | ||||
|                                     }, | ||||
|                                     repo.getZipUrl(), | ||||
|                                     Utils.getLegalFilename(filename))) | ||||
|                             .setNeutralButton(R.string.download, (d, i) -> Utils.dlAndReceive( | ||||
|                                     context, | ||||
|                                     new DownloadReceiver() { | ||||
|                                         @Override | ||||
|                                         public void onDownloadDone(Uri uri) { | ||||
|                                             new ProcessRepoZip((Activity) context, uri, false).exec(); | ||||
|                                         } | ||||
|                                     }, | ||||
|                                     repo.getZipUrl(), | ||||
|                                     Utils.getLegalFilename(filename))) | ||||
|                             .setNegativeButton(R.string.no_thanks, null) | ||||
|                             .show(); | ||||
|                 }); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getItemViewType(int position) { | ||||
|         for (int i : sectionList) { | ||||
|             if (position == i) | ||||
|                 return SECTION_TYPE; | ||||
|         } | ||||
|         return REPO_TYPE; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getItemCount() { | ||||
|         return size; | ||||
|     } | ||||
|  | ||||
|     public void filter(ValueSortedMap<String, Module> moduleMap, String s) { | ||||
|         mUpdateRepos.clear(); | ||||
|         mInstalledRepos.clear(); | ||||
|         mOthersRepos.clear(); | ||||
|         sectionList[0] = sectionList[1] = sectionList[2] = 0; | ||||
|         for (Repo repo : repoMap.values()) { | ||||
|             if (repo.getName().toLowerCase().contains(s.toLowerCase()) | ||||
|                     || repo.getAuthor().toLowerCase().contains(s.toLowerCase()) | ||||
|                     || repo.getDescription().toLowerCase().contains(s.toLowerCase()) | ||||
|                     ) { | ||||
|                 // Passed the filter | ||||
|                 Module module = moduleMap.get(repo.getId()); | ||||
|                 if (module != null) { | ||||
|                     if (repo.getVersionCode() > module.getVersionCode()) { | ||||
|                         // Updates | ||||
|                         mUpdateRepos.add(repo); | ||||
|                     } else { | ||||
|                         mInstalledRepos.add(repo); | ||||
|                     } | ||||
|                 } else { | ||||
|                     mOthersRepos.add(repo); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         sectionList[0] = mUpdateRepos.isEmpty() ? -1 : 0; | ||||
|         size = mUpdateRepos.isEmpty() ? 0 : mUpdateRepos.size() + 1; | ||||
|         sectionList[1] = mInstalledRepos.isEmpty() ? -1 : size; | ||||
|         size += mInstalledRepos.isEmpty() ? 0 : mInstalledRepos.size() + 1; | ||||
|         sectionList[2] = mOthersRepos.isEmpty() ? -1 : size; | ||||
|         size += mOthersRepos.isEmpty() ? 0 : mOthersRepos.size() + 1; | ||||
|  | ||||
|         notifyDataSetChanged(); | ||||
|     } | ||||
|  | ||||
|     private Repo getRepo(int position) { | ||||
|         if (!mUpdateRepos.isEmpty()) position -= 1; | ||||
|         if (position < mUpdateRepos.size()) return mUpdateRepos.get(position); | ||||
|         position -= mUpdateRepos.size(); | ||||
|         if (!mInstalledRepos.isEmpty()) position -= 1; | ||||
|         if (position < mInstalledRepos.size()) return mInstalledRepos.get(position); | ||||
|         position -= mInstalledRepos.size(); | ||||
|         if (!mOthersRepos.isEmpty()) position -= 1; | ||||
|         return mOthersRepos.get(position); | ||||
|     } | ||||
|  | ||||
|     static class SectionHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         @BindView(R.id.section_text) TextView sectionText; | ||||
|  | ||||
|         SectionHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             ButterKnife.bind(this, itemView); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static class RepoHolder 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; | ||||
|  | ||||
|         RepoHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             ButterKnife.bind(this, itemView); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,197 +0,0 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| 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.animation.Animation; | ||||
| import android.view.animation.RotateAnimation; | ||||
| import android.widget.ImageView; | ||||
| 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.components.ExpandableViewHolder; | ||||
| 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.getExpanded()) { | ||||
|                     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); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // Wrapper class | ||||
|     static class LogViewHolder extends ChildViewHolder { | ||||
|  | ||||
|         private InternalViewHolder expandableViewHolder; | ||||
|  | ||||
|         @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; | ||||
|  | ||||
|         LogViewHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             ButterKnife.bind(this, itemView); | ||||
|             expandableViewHolder = new InternalViewHolder(itemView); | ||||
|         } | ||||
|  | ||||
|         private class InternalViewHolder extends ExpandableViewHolder { | ||||
|  | ||||
|             InternalViewHolder(View itemView) { | ||||
|                 super(itemView); | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public void setExpandLayout(View itemView) { | ||||
|                 expandLayout = itemView.findViewById(R.id.expand_layout); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         private boolean getExpanded() { | ||||
|             return expandableViewHolder.mExpanded; | ||||
|         } | ||||
|  | ||||
|         private void setExpanded(boolean expanded) { | ||||
|             expandableViewHolder.setExpanded(expanded); | ||||
|         } | ||||
|  | ||||
|         private void expand() { | ||||
|             expandableViewHolder.expand(); | ||||
|         } | ||||
|  | ||||
|         private void collapse() { | ||||
|             expandableViewHolder.collapse(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,57 +0,0 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.content.Context; | ||||
|  | ||||
| import com.topjohnwu.magisk.BuildConfig; | ||||
| 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/update/magisk_update.json"; | ||||
|  | ||||
|     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.remoteMagiskVersionString = magisk.getString("version"); | ||||
|             magiskManager.remoteMagiskVersionCode = magisk.getInt("versionCode"); | ||||
|             magiskManager.magiskLink = magisk.getString("link"); | ||||
|             magiskManager.releaseNoteLink = magisk.getString("note"); | ||||
|             JSONObject manager = json.getJSONObject("app"); | ||||
|             magiskManager.remoteManagerVersionString = manager.getString("version"); | ||||
|             magiskManager.remoteManagerVersionCode = manager.getInt("versionCode"); | ||||
|             magiskManager.managerLink = manager.getString("link"); | ||||
|         } catch (JSONException ignored) {} | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostExecute(Void v) { | ||||
|         if (showNotification && magiskManager.updateNotification) { | ||||
|             if (BuildConfig.VERSION_CODE < magiskManager.remoteManagerVersionCode) { | ||||
|                 Utils.showManagerUpdate(magiskManager); | ||||
|             } else if (magiskManager.magiskVersionCode < magiskManager.remoteMagiskVersionCode) { | ||||
|                 Utils.showMagiskUpdate(magiskManager); | ||||
|             } | ||||
|         } | ||||
|         magiskManager.updateCheckDone.trigger(); | ||||
|         super.onPostExecute(v); | ||||
|     } | ||||
| } | ||||
| @@ -1,124 +0,0 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.net.Uri; | ||||
| import android.text.TextUtils; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.utils.AdaptiveList; | ||||
| 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 ParallelTask<Void, String, Integer> { | ||||
|  | ||||
|     private Uri mUri; | ||||
|     private File mCachedFile, mScriptFile, mCheckFile; | ||||
|  | ||||
|     private String mFilename; | ||||
|     private AdaptiveList<String> mList; | ||||
|  | ||||
|     public FlashZip(Activity context, Uri uri, AdaptiveList<String> list) { | ||||
|         super(context); | ||||
|         mUri = uri; | ||||
|         mList = list; | ||||
|  | ||||
|         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 Exception { | ||||
|         mList.add(magiskManager.getString(R.string.copying_msg)); | ||||
|  | ||||
|         mCachedFile.delete(); | ||||
|         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); | ||||
|         } catch (FileNotFoundException e) { | ||||
|             mList.add("! Invalid Uri"); | ||||
|             throw e; | ||||
|         } catch (IOException e) { | ||||
|             mList.add("! Cannot copy to cache"); | ||||
|             throw e; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private boolean unzipAndCheck() throws Exception { | ||||
|         ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android"); | ||||
|         List<String> ret = Utils.readFile(magiskManager.shell, mCheckFile.getPath()); | ||||
|         return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK"); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPreExecute() { | ||||
|         // UI updates must run in the UI thread | ||||
|         mList.setCallback(this::publishProgress); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onProgressUpdate(String... values) { | ||||
|         mList.updateView(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Integer doInBackground(Void... voids) { | ||||
|         try { | ||||
|             copyToCache(); | ||||
|             if (!unzipAndCheck()) return 0; | ||||
|             mList.add(magiskManager.getString(R.string.zip_install_progress_msg, mFilename)); | ||||
|             magiskManager.shell.su(mList, | ||||
|                     "BOOTMODE=true sh " + mScriptFile + " dummy 1 " + mCachedFile + | ||||
|                             " && echo 'Success!' || echo 'Failed!'" | ||||
|             ); | ||||
|             if (TextUtils.equals(mList.get(mList.size() - 1), "Success!")) | ||||
|                 return 1; | ||||
|         } catch (Exception e) { | ||||
|             e.printStackTrace(); | ||||
|         } | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     // -1 = error, manual install; 0 = invalid zip; 1 = success | ||||
|     @Override | ||||
|     protected void onPostExecute(Integer result) { | ||||
|         magiskManager.shell.su_raw( | ||||
|                 "rm -rf " + mCachedFile.getParent() + "/*", | ||||
|                 "rm -rf " + MagiskManager.TMP_FOLDER_PATH | ||||
|         ); | ||||
|         switch (result) { | ||||
|             case -1: | ||||
|                 mList.add(magiskManager.getString(R.string.install_error)); | ||||
|                 Utils.showUriSnack(activity, mUri); | ||||
|                 break; | ||||
|             case 0: | ||||
|                 mList.add(magiskManager.getString(R.string.invalid_zip)); | ||||
|                 break; | ||||
|             case 1: | ||||
|                 onSuccess(); | ||||
|                 break; | ||||
|         } | ||||
|         super.onPostExecute(result); | ||||
|     } | ||||
|  | ||||
|     protected void onSuccess() { | ||||
|         new LoadModules(activity).exec(); | ||||
|     } | ||||
| } | ||||
| @@ -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 ParallelTask<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.shell, MagiskManager.MAGISK_PATH)) { | ||||
|             Logger.dev("LoadModules: Adding modules from " + path); | ||||
|             try { | ||||
|                 Module module = new Module(magiskManager.shell, 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(); | ||||
|         super.onPostExecute(v); | ||||
|     } | ||||
| } | ||||
| @@ -1,189 +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(); | ||||
|         super.onPostExecute(v); | ||||
|     } | ||||
| } | ||||
| @@ -1,55 +0,0 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.app.Activity; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| public class MagiskHide extends ParallelTask<Object, Void, Void> { | ||||
|  | ||||
|     private boolean isList = false; | ||||
|  | ||||
|     public MagiskHide(Activity context) { | ||||
|         super(context); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected Void doInBackground(Object... params) { | ||||
|         String command = (String) params[0]; | ||||
|         List<String> ret = magiskManager.shell.su("magiskhide --" + command); | ||||
|         if (isList) { | ||||
|             magiskManager.magiskHideList = ret; | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onPostExecute(Void v) { | ||||
|         if (isList) { | ||||
|             magiskManager.magiskHideDone.trigger(); | ||||
|         } | ||||
|         super.onPostExecute(v); | ||||
|     } | ||||
|  | ||||
|     public void add(CharSequence packageName) { | ||||
|         exec("add " + packageName); | ||||
|     } | ||||
|  | ||||
|     public void rm(CharSequence packageName) { | ||||
|         exec("rm " + packageName); | ||||
|     } | ||||
|  | ||||
|     public void enable() { | ||||
|         exec("enable"); | ||||
|     } | ||||
|  | ||||
|     public void disable() { | ||||
|         exec("disable"); | ||||
|     } | ||||
|  | ||||
|     public void list() { | ||||
|         isList = true; | ||||
|         if (magiskManager == null) return; | ||||
|         exec("ls"); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,98 +0,0 @@ | ||||
| package com.topjohnwu.magisk.asyncs; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.app.ProgressDialog; | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import com.topjohnwu.magisk.FlashActivity; | ||||
| 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.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) { | ||||
|         try { | ||||
|  | ||||
|             // Create temp file | ||||
|             File temp1 = new File(magiskManager.getCacheDir(), "1.zip"); | ||||
|             File temp2 = new File(magiskManager.getCacheDir(), "2.zip"); | ||||
|             magiskManager.getCacheDir().mkdirs(); | ||||
|             temp1.createNewFile(); | ||||
|             temp2.createNewFile(); | ||||
|  | ||||
|             // First remove top folder in Github source zip, Uri -> temp1 | ||||
|             ZipUtils.removeTopFolder(activity.getContentResolver().openInputStream(mUri), temp1); | ||||
|  | ||||
|             // Then sign the zip for the first time, temp1 -> temp2 | ||||
|             ZipUtils.signZip(activity, temp1, temp2, false); | ||||
|  | ||||
|             // Adjust the zip to prevent unzip issues, temp2 -> temp1 | ||||
|             ZipUtils.zipAdjust(temp2.getPath(), temp1.getPath()); | ||||
|  | ||||
|             // Finally, sign the whole zip file again, temp1 -> temp2 | ||||
|             ZipUtils.signZip(activity, temp1, temp2, true); | ||||
|  | ||||
|             // Write it back to the downloaded zip, temp2 -> Uri | ||||
|             FileInputStream in = new FileInputStream(temp2); | ||||
|             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) { | ||||
|                 magiskManager.startActivity(new Intent(magiskManager, FlashActivity.class).setData(mUri)); | ||||
|             } else { | ||||
|                 Utils.showUriSnack(activity, mUri); | ||||
|             } | ||||
|         } else { | ||||
|             magiskManager.toast(R.string.process_error, Toast.LENGTH_LONG); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,30 +0,0 @@ | ||||
| package com.topjohnwu.magisk.components; | ||||
|  | ||||
| import android.support.v7.app.AppCompatActivity; | ||||
| import android.view.WindowManager; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.R; | ||||
|  | ||||
| public class Activity extends AppCompatActivity { | ||||
|  | ||||
|     @Override | ||||
|     public MagiskManager getApplicationContext() { | ||||
|         return (MagiskManager) super.getApplicationContext(); | ||||
|     } | ||||
|  | ||||
|     protected 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,77 +0,0 @@ | ||||
| package com.topjohnwu.magisk.components; | ||||
|  | ||||
| import android.animation.ValueAnimator; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.view.ViewTreeObserver; | ||||
|  | ||||
| public abstract class ExpandableViewHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|     protected ViewGroup expandLayout; | ||||
|     private ValueAnimator expandAnimator, collapseAnimator; | ||||
|     private static int expandHeight = 0; | ||||
|  | ||||
|     public boolean mExpanded = false; | ||||
|  | ||||
|     public ExpandableViewHolder(View itemView) { | ||||
|         super(itemView); | ||||
|         setExpandLayout(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); | ||||
|                         expandAnimator = slideAnimator(0, expandHeight); | ||||
|                         collapseAnimator = slideAnimator(expandHeight, 0); | ||||
|                         return true; | ||||
|                     } | ||||
|  | ||||
|                 }); | ||||
|     } | ||||
|  | ||||
|     public 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); | ||||
|     } | ||||
|  | ||||
|     public void expand() { | ||||
|         if (mExpanded) return; | ||||
|         expandLayout.setVisibility(View.VISIBLE); | ||||
|         expandAnimator.start(); | ||||
|         mExpanded = true; | ||||
|     } | ||||
|  | ||||
|     public void collapse() { | ||||
|         if (!mExpanded) return; | ||||
|         collapseAnimator.start(); | ||||
|         mExpanded = false; | ||||
|     } | ||||
|  | ||||
|     public abstract void setExpandLayout(View itemView); | ||||
|  | ||||
|     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,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()); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,31 +0,0 @@ | ||||
| package com.topjohnwu.magisk.components; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.support.v7.app.AlertDialog; | ||||
|  | ||||
| import com.topjohnwu.magisk.MagiskManager; | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| import us.feras.mdv.MarkdownView; | ||||
|  | ||||
| public class MarkDownWindow { | ||||
|  | ||||
|     public MarkDownWindow(String title, String url, Context context) { | ||||
|         MagiskManager magiskManager = Utils.getMagiskManager(context); | ||||
|         AlertDialog.Builder alert = new AlertDialog.Builder(context); | ||||
|         alert.setTitle(title); | ||||
|      | ||||
|         Logger.dev("WebView: URL = " + url); | ||||
|  | ||||
|         MarkdownView md = new MarkdownView(context); | ||||
|         md.loadMarkdownFile(url, "file:///android_asset/" + | ||||
|                 (magiskManager.isDarkTheme ? "dark" : "light") + ".css"); | ||||
|  | ||||
|         alert.setView(md); | ||||
|         alert.setNegativeButton(R.string.close, (dialog, id) -> dialog.dismiss()); | ||||
|         alert.show(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,93 +0,0 @@ | ||||
| package com.topjohnwu.magisk.database; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.database.Cursor; | ||||
| import android.database.sqlite.SQLiteDatabase; | ||||
| import android.database.sqlite.SQLiteOpenHelper; | ||||
|  | ||||
| import com.topjohnwu.magisk.module.Repo; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.ValueSortedMap; | ||||
|  | ||||
| import java.util.Collection; | ||||
|  | ||||
| public class RepoDatabaseHelper extends SQLiteOpenHelper { | ||||
|  | ||||
|     private static final int DATABASE_VER = 2; | ||||
|     private static final String TABLE_NAME = "repos"; | ||||
|     private static final int MIN_TEMPLATE_VER = 3; | ||||
|  | ||||
|     public RepoDatabaseHelper(Context context) { | ||||
|         super(context, "repo.db", null, DATABASE_VER); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(SQLiteDatabase db) { | ||||
|         onUpgrade(db, 0, DATABASE_VER); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { | ||||
|         if (oldVersion == 0) { | ||||
|             db.execSQL( | ||||
|                     "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " + | ||||
|                     "(id TEXT, name TEXT, version TEXT, versionCode INT, " + | ||||
|                     "author TEXT, description TEXT, repo_name TEXT, last_update INT, " + | ||||
|                     "PRIMARY KEY(id))"); | ||||
|             oldVersion++; | ||||
|         } | ||||
|         if (oldVersion == 1) { | ||||
|             db.execSQL("ALTER TABLE " + TABLE_NAME + " ADD template INT"); | ||||
|             oldVersion++; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void addRepoMap(ValueSortedMap<String, Repo> map) { | ||||
|         SQLiteDatabase db = getWritableDatabase(); | ||||
|         Collection<Repo> list = map.values(); | ||||
|         for (Repo repo : list) { | ||||
|             Logger.dev("Add to DB: " + repo.getId()); | ||||
|             db.replace(TABLE_NAME, null, repo.getContentValues()); | ||||
|         } | ||||
|         db.close(); | ||||
|     } | ||||
|  | ||||
|     public void clearRepo() { | ||||
|         SQLiteDatabase db = getWritableDatabase(); | ||||
|         db.delete(TABLE_NAME, null, null); | ||||
|         db.close(); | ||||
|     } | ||||
|  | ||||
|     public void removeRepo(ValueSortedMap<String, Repo> map) { | ||||
|         SQLiteDatabase db = getWritableDatabase(); | ||||
|         Collection<Repo> list = map.values(); | ||||
|         for (Repo repo : list) { | ||||
|             Logger.dev("Remove from DB: " + repo.getId()); | ||||
|             db.delete(TABLE_NAME, "id=?", new String[] { repo.getId() }); | ||||
|         } | ||||
|         db.close(); | ||||
|     } | ||||
|  | ||||
|     public ValueSortedMap<String, Repo> getRepoMap() { | ||||
|         return getRepoMap(true); | ||||
|     } | ||||
|  | ||||
|     public ValueSortedMap<String, Repo> getRepoMap(boolean filtered) { | ||||
|         ValueSortedMap<String, Repo> ret = new ValueSortedMap<>(); | ||||
|         SQLiteDatabase db = getReadableDatabase(); | ||||
|         Repo repo; | ||||
|         try (Cursor c = db.query(TABLE_NAME, null, null, null, null, null, null)) { | ||||
|             while (c.moveToNext()) { | ||||
|                 repo = new Repo(c); | ||||
|                 if (repo.getTemplateVersion() < MIN_TEMPLATE_VER && filtered) { | ||||
|                     Logger.dev("Outdated repo: " + repo.getId()); | ||||
|                 } else { | ||||
|                     // Logger.dev("Load from DB: " + repo.getId()); | ||||
|                     ret.put(repo.getId(), repo); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         db.close(); | ||||
|         return ret; | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,68 +0,0 @@ | ||||
| package com.topjohnwu.magisk.module; | ||||
|  | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| public class Module extends BaseModule { | ||||
|  | ||||
|     private String mRemoveFile, mDisableFile, mUpdateFile; | ||||
|     private boolean mEnable, mRemove, mUpdated; | ||||
|  | ||||
|     public Module(Shell shell, String path) throws CacheModException { | ||||
|  | ||||
|         parseProps(Utils.readFile(shell, path + "/module.prop")); | ||||
|  | ||||
|         mRemoveFile = path + "/remove"; | ||||
|         mDisableFile = path + "/disable"; | ||||
|         mUpdateFile = path + "/update"; | ||||
|  | ||||
|         if (getId() == null) { | ||||
|             int sep = path.lastIndexOf('/'); | ||||
|             setId(path.substring(sep + 1)); | ||||
|         } | ||||
|  | ||||
|         if (getName() == null) { | ||||
|             setName(getId()); | ||||
|         } | ||||
|  | ||||
|         Logger.dev("Creating Module, id: " + getId()); | ||||
|  | ||||
|         mEnable = !Utils.itemExist(shell, mDisableFile); | ||||
|         mRemove = Utils.itemExist(shell, mRemoveFile); | ||||
|         mUpdated = Utils.itemExist(shell, mUpdateFile); | ||||
|     } | ||||
|  | ||||
|     public void createDisableFile(Shell shell) { | ||||
|         mEnable = false; | ||||
|         Utils.createFile(shell, mDisableFile); | ||||
|     } | ||||
|  | ||||
|     public void removeDisableFile(Shell shell) { | ||||
|         mEnable = true; | ||||
|         Utils.removeItem(shell, mDisableFile); | ||||
|     } | ||||
|  | ||||
|     public boolean isEnabled() { | ||||
|         return mEnable; | ||||
|     } | ||||
|  | ||||
|     public void createRemoveFile(Shell shell) { | ||||
|         mRemove = true; | ||||
|         Utils.createFile(shell, mRemoveFile); | ||||
|     } | ||||
|  | ||||
|     public void deleteRemoveFile(Shell shell) { | ||||
|         mRemove = false; | ||||
|         Utils.removeItem(shell, mRemoveFile); | ||||
|     } | ||||
|  | ||||
|     public boolean willBeRemoved() { | ||||
|         return mRemove; | ||||
|     } | ||||
|  | ||||
|     public boolean isUpdated() { | ||||
|         return mUpdated; | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,74 +0,0 @@ | ||||
| package com.topjohnwu.magisk.module; | ||||
|  | ||||
| import android.content.ContentValues; | ||||
| import android.database.Cursor; | ||||
|  | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.WebService; | ||||
|  | ||||
| import java.util.Date; | ||||
|  | ||||
| public class Repo extends BaseModule { | ||||
|  | ||||
|     private static final String FILE_URL = "https://raw.githubusercontent.com/Magisk-Modules-Repo/%s/master/%s"; | ||||
|     private static final String ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip"; | ||||
|  | ||||
|     private String repoName; | ||||
|     private Date mLastUpdate; | ||||
|  | ||||
|     public Repo(String name, Date lastUpdate) throws CacheModException { | ||||
|         mLastUpdate = lastUpdate; | ||||
|         repoName = name; | ||||
|         update(); | ||||
|     } | ||||
|  | ||||
|     public Repo(Cursor c) { | ||||
|         super(c); | ||||
|         repoName = c.getString(c.getColumnIndex("repo_name")); | ||||
|         mLastUpdate = new Date(c.getLong(c.getColumnIndex("last_update"))); | ||||
|     } | ||||
|  | ||||
|     public void update() throws CacheModException { | ||||
|         String props = WebService.request(getManifestUrl(), WebService.GET); | ||||
|         String lines[] = props.split("\\n"); | ||||
|         parseProps(lines); | ||||
|         Logger.dev("Repo: Fetching prop: " + getId()); | ||||
|     } | ||||
|  | ||||
|     public void update(Date lastUpdate) throws CacheModException { | ||||
|         if (lastUpdate.after(mLastUpdate)) { | ||||
|             mLastUpdate = lastUpdate; | ||||
|             update(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public ContentValues getContentValues() { | ||||
|         ContentValues values = new ContentValues(); | ||||
|         values.put("id", getId()); | ||||
|         values.put("name", getName()); | ||||
|         values.put("version", getVersion()); | ||||
|         values.put("versionCode", getVersionCode()); | ||||
|         values.put("author", getAuthor()); | ||||
|         values.put("description", getDescription()); | ||||
|         values.put("repo_name", repoName); | ||||
|         values.put("last_update", mLastUpdate.getTime()); | ||||
|         values.put("template", getTemplateVersion()); | ||||
|         return values; | ||||
|     } | ||||
|  | ||||
|     public String getZipUrl() { | ||||
|         return String.format(ZIP_URL, repoName); | ||||
|     } | ||||
|  | ||||
|     public String getManifestUrl() { | ||||
|         return String.format(FILE_URL, repoName, "module.prop"); | ||||
|     } | ||||
|  | ||||
|     public String getDetailUrl() { | ||||
|         return String.format(FILE_URL, repoName, "README.md"); | ||||
|     } | ||||
|  | ||||
|     public Date getLastUpdate() { | ||||
|         return mLastUpdate; | ||||
|     } | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user