mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-10-31 10:40:52 +01:00 
			
		
		
		
	Compare commits
	
		
			269 Commits
		
	
	
		
			manager-v3
			...
			manager-v5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | cc14a1c361 | ||
|   | bcdface60d | ||
|   | 4dc9419d2e | ||
|   | d2bcac813e | ||
|   | 080c37a7f6 | ||
|   | f9a3838db6 | ||
|   | 1e61db104b | ||
|   | 30a9c7718d | ||
|   | 34b052b5d3 | ||
|   | aaa12853ad | ||
|   | b0ab55b0bf | ||
|   | d2f8496f4e | ||
|   | 1a69b16d36 | ||
|   | b5e8673e62 | ||
|   | 264c6a50b6 | ||
|   | 493642eb38 | ||
|   | 28d42b9164 | ||
|   | 42f29062ca | ||
|   | c4377ed6c2 | ||
|   | 7d283ed65f | ||
|   | bf1f941e50 | ||
|   | 789fef34ba | ||
|   | 1daf5a611c | ||
|   | 6aed1db67e | ||
|   | cf68854770 | ||
|   | 711392c73b | ||
|   | 9573c32481 | ||
|   | a15f80f79d | ||
|   | 23e7475f06 | ||
|   | 1eb571b787 | ||
|   | dd3b716d85 | ||
|   | 28649c07e3 | ||
|   | 961e02be0d | ||
|   | a161491bfd | ||
|   | e0b4d1c1e4 | ||
|   | fd4aaab137 | ||
|   | 42d14d5ca2 | ||
|   | d3ff482c9b | ||
|   | f682368eeb | ||
|   | 4a5d033efb | ||
|   | 343161b195 | ||
|   | bc576a9659 | ||
|   | 19e407fcc4 | ||
|   | bc7327d004 | ||
|   | 666fa1c797 | ||
|   | 0eda4a7821 | ||
|   | 862058fd2b | ||
|   | 69e5bcd57d | ||
|   | efeddda328 | ||
|   | ff6938280e | ||
|   | 1e4425b30f | ||
|   | b5d1d8cdad | ||
|   | 029be5ccca | ||
|   | 29c2d785b5 | ||
|   | abda8cfa32 | ||
|   | 44e7d79d4c | ||
|   | 9a1dc8ee0e | ||
|   | 27879c3f01 | ||
|   | 29096eb5d7 | ||
|   | a573baea03 | ||
|   | 5af07c4531 | ||
|   | 44e36feb09 | ||
|   | 2a7d996881 | ||
|   | 738f943a68 | ||
|   | 47e62a5681 | ||
|   | 1ecbfd7590 | ||
|   | 67c139a04b | ||
|   | 31cc008249 | ||
|   | 9cb026439d | ||
|   | e6f10176c6 | ||
|   | 0917c79470 | ||
|   | 597baa986d | ||
|   | 75cc4b4843 | ||
|   | aac088d496 | ||
|   | a822e5bbc5 | ||
|   | c527249c21 | ||
|   | 9ef798f534 | ||
|   | e69b99f089 | ||
|   | 55b8079e86 | ||
|   | e272dbe9af | ||
|   | 962f8354ac | ||
|   | 20e4a960f7 | ||
|   | 82249cb50a | ||
|   | fad417e553 | ||
|   | 5ba692f50c | ||
|   | 907e01e524 | ||
|   | b8ed23efa7 | ||
|   | 2b3bbf7e67 | ||
|   | 464fe627a3 | ||
|   | 6a9e39c470 | ||
|   | 7fec9a3cc6 | ||
|   | 008f6ef462 | ||
|   | 2440c108ca | ||
|   | 430baad8a4 | ||
|   | 51132e74b4 | ||
|   | a4f33e106a | ||
|   | baba3190e0 | ||
|   | 47b13aa5ea | ||
|   | ae88d3054d | ||
|   | 411b600e14 | ||
|   | 0a0ad9a184 | ||
|   | 234bead59e | ||
|   | 76de310986 | ||
|   | 817f050bcd | ||
|   | 60ae685d1e | ||
|   | 4c7bdbb284 | ||
|   | 435251ca41 | ||
|   | 324a0dd38f | ||
|   | cc77d93918 | ||
|   | 0ea7d8bd8c | ||
|   | 849b217143 | ||
|   | 9af6efba59 | ||
|   | 079d6f06ef | ||
|   | 9cf0757689 | ||
|   | b54c438948 | ||
|   | c3ff4bfdad | ||
|   | 5d62e066e2 | ||
|   | e94219c5a3 | ||
|   | 8ed9634adf | ||
|   | 0aefa9599f | ||
|   | e279cf0575 | ||
|   | a3f0ef8e77 | ||
|   | 8eba05ed4a | ||
|   | 2f78155723 | ||
|   | 6785221479 | ||
|   | 9bc410dd3d | ||
|   | 2491ab6bf9 | ||
|   | f615ed40cd | ||
|   | 430f2cafc1 | ||
|   | 0ad049da88 | ||
|   | 2c7691567b | ||
|   | 1d70d0fe94 | ||
|   | ac44f05811 | ||
|   | d99252f394 | ||
|   | b58c7ba7c5 | ||
|   | 8c5acd1a0a | ||
|   | b9b1ebf18c | ||
|   | 8ca132cef0 | ||
|   | a03bb90754 | ||
|   | d1c939f48a | ||
|   | 21b11f1b48 | ||
|   | 23c84a7803 | ||
|   | f9ab060403 | ||
|   | df7a5bf149 | ||
|   | c4afa069df | ||
|   | 1bfafdb44f | ||
|   | 1ef5bd7076 | ||
|   | 29176fa4f4 | ||
|   | 958c95732b | ||
|   | 44b0d4127c | ||
|   | 1418ec2416 | ||
|   | b51978f51c | ||
|   | b07361580a | ||
|   | d1b5ebad7d | ||
|   | f4ce813de9 | ||
|   | b44ac994d8 | ||
|   | 333948814c | ||
|   | 1a51ad6e01 | ||
|   | 22a5c11f0d | ||
|   | 51b22d1ad4 | ||
|   | bef5969580 | ||
|   | c6bf7bb9cd | ||
|   | 2a84d92cbf | ||
|   | 62de36b0da | ||
|   | 03a9aaeff7 | ||
|   | 45765e292d | ||
|   | 6e28a26015 | ||
|   | 9150bf720d | ||
|   | 845864679c | ||
|   | b3b2149ebb | ||
|   | 0886dca385 | ||
|   | 53198ba4a7 | ||
|   | a9652ee1fd | ||
|   | 75caf2f01c | ||
|   | 65bab2666e | ||
|   | 6d93ae399a | ||
|   | 7239c2e31a | ||
|   | 43b7ef8110 | ||
|   | 99ef0b8cb4 | ||
|   | 0efb4da0ee | ||
|   | ed7920d61e | ||
|   | c0379c8e25 | ||
|   | 00a0e64fdd | ||
|   | 0dc60debea | ||
|   | c44ae5888c | ||
|   | b9495cd1bb | ||
|   | bfec381933 | ||
|   | 2dddb8df69 | ||
|   | d30397e9c0 | ||
|   | d9597549fd | ||
|   | 13512b4146 | ||
|   | 49e546919a | ||
|   | 586015c2ed | ||
|   | 4a7e067d1a | ||
|   | 9bc0b7f183 | ||
|   | cd4dfc9861 | ||
|   | 09bdbc1224 | ||
|   | 978b3a64c5 | ||
|   | 651547ef20 | ||
|   | b4d95977d0 | ||
|   | 5d8bb897db | ||
|   | 84c8ecb372 | ||
|   | 61abe5b948 | ||
|   | a5b573eaaa | ||
|   | cbb32f82eb | ||
|   | ca9334b2df | ||
|   | 959ed7f866 | ||
|   | a5c0411be0 | ||
|   | 32e1303742 | ||
|   | 7263b6fe89 | ||
|   | 46a4070f84 | ||
|   | c3c155a1ed | ||
|   | b067105660 | ||
|   | 15ca18848e | ||
|   | 67c9e2ead6 | ||
|   | 3681177be4 | ||
|   | 6eb814ef0b | ||
|   | bcc695234c | ||
|   | ad16a6fc1b | ||
|   | 478b7eeb65 | ||
|   | 151a153dc9 | ||
|   | ad131854ca | ||
|   | 0bd0eb9e59 | ||
|   | cf16fd0104 | ||
|   | 21b00ac6ca | ||
|   | 57e6f3080c | ||
|   | 89744100ce | ||
|   | a718f9bbfd | ||
|   | e81bc4f044 | ||
|   | 4dbacd79ae | ||
|   | ae74d54451 | ||
|   | dc316c5669 | ||
|   | e9f04256c9 | ||
|   | e1aabd70e8 | ||
|   | a9dc1b32e0 | ||
|   | 01d847ae4e | ||
|   | 61e2c3444a | ||
|   | 5363b0f810 | ||
|   | f0e1a8823e | ||
|   | 7be5937aa0 | ||
|   | 8f43055b0e | ||
|   | 953a81b299 | ||
|   | 1d34ae7934 | ||
|   | 2cabb2666b | ||
|   | 0b59bb1a29 | ||
|   | c1e7d74b96 | ||
|   | cc262d6595 | ||
|   | 61d43b118b | ||
|   | 989d8181dd | ||
|   | cffc157d98 | ||
|   | 2a70619577 | ||
|   | b91919bffa | ||
|   | fb7a4bf880 | ||
|   | 4b41799a90 | ||
|   | 123f39a21b | ||
|   | cadab12737 | ||
|   | 742055c43b | ||
|   | fa73b41fa7 | ||
|   | a474eafe84 | ||
|   | 442fcf921c | ||
|   | fb0923f3ab | ||
|   | 5bb943f845 | ||
|   | a3109953d0 | ||
|   | ff266c8c79 | ||
|   | ef2e02098d | ||
|   | 93598d3a51 | ||
|   | 53aebcfb1e | ||
|   | bb2467d2ac | ||
|   | 05c063b61d | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -3,6 +3,6 @@ | ||||
| /local.properties | ||||
| .idea/ | ||||
| /build | ||||
| app/app-release.apk | ||||
| app/release | ||||
| *.hprof | ||||
| app/.externalNativeBuild/ | ||||
|   | ||||
							
								
								
									
										10
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,4 +1,6 @@ | ||||
| # Magisk Manager | ||||
| The project can only be compiled on Android Studio Version 2.2.0+   | ||||
| I use Java 8 features, which requires Jack compiler and that's only available in 2.2.0+ | ||||
| Also, you need to install CMake and NDK to build the zipadjust library for zip preprocessing | ||||
| # 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) | ||||
|   | ||||
| @@ -1,5 +0,0 @@ | ||||
| cmake_minimum_required(VERSION 3.6) | ||||
| add_library(zipadjust SHARED src/main/jni/zipadjust.c) | ||||
| find_library(libz z) | ||||
| find_library(liblog log) | ||||
| target_link_libraries(zipadjust ${libz} ${liblog}) | ||||
| @@ -1,31 +1,25 @@ | ||||
| apply plugin: 'com.android.application' | ||||
|  | ||||
| android { | ||||
|     compileSdkVersion 25 | ||||
|     buildToolsVersion "25.0.2" | ||||
|     compileSdkVersion 26 | ||||
|     buildToolsVersion "26.0.0" | ||||
|  | ||||
|     defaultConfig { | ||||
|         applicationId "com.topjohnwu.magisk" | ||||
|         minSdkVersion 21 | ||||
|         targetSdkVersion 25 | ||||
|         versionCode 11 | ||||
|         versionName "3.0" | ||||
|         jackOptions { | ||||
|             enabled true | ||||
|         } | ||||
|         targetSdkVersion 26 | ||||
|         versionCode 46 | ||||
|         versionName "5.0.6" | ||||
|         ndk { | ||||
|             moduleName 'zipadjust' | ||||
|             abiFilters 'x86', 'x86_64', 'armeabi', 'arm64-v8a' | ||||
|             abiFilters 'x86', 'armeabi-v7a' | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     compileOptions { | ||||
|         incremental false | ||||
|     } | ||||
|  | ||||
|     buildTypes { | ||||
|         release { | ||||
|             minifyEnabled true | ||||
|             shrinkResources true | ||||
|             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' | ||||
|         } | ||||
|     } | ||||
| @@ -34,31 +28,36 @@ android { | ||||
|         targetCompatibility JavaVersion.VERSION_1_8 | ||||
|     } | ||||
|     dexOptions { | ||||
|         preDexLibraries = false | ||||
|         preDexLibraries true | ||||
|         javaMaxHeapSize "2g" | ||||
|     } | ||||
|     externalNativeBuild { | ||||
|         cmake { | ||||
|             path 'CMakeLists.txt' | ||||
|             path 'src/main/jni/CMakeLists.txt' | ||||
|         } | ||||
|     } | ||||
|     lintOptions { | ||||
|         disable 'MissingTranslation' | ||||
|     } | ||||
| } | ||||
| repositories { | ||||
|     jcenter() | ||||
|     maven { url "https://jitpack.io" } | ||||
|     maven { url "https://maven.google.com" } | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
|     compile fileTree(include: ['*.jar'], dir: 'libs') | ||||
|     compile 'com.android.support:recyclerview-v7:25.1.0' | ||||
|     compile 'com.android.support:cardview-v7:25.1.0' | ||||
|     compile 'com.android.support:design:25.1.0' | ||||
|     compile 'com.jakewharton:butterknife:8.4.0' | ||||
|     compile 'com.google.code.gson:gson:2.8.0' | ||||
|     compile 'com.github.clans:fab:1.6.4' | ||||
|     compile 'com.madgag.spongycastle:core:1.54.0.0' | ||||
|     compile 'com.madgag.spongycastle:prov:1.54.0.0' | ||||
|     compile 'com.madgag.spongycastle:pkix:1.54.0.0' | ||||
|     compile 'com.madgag.spongycastle:pg:1.54.0.0' | ||||
|     compile 'com.google.android.gms:play-services-safetynet:9.0.1' | ||||
|     annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0' | ||||
|     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' | ||||
| } | ||||
|   | ||||
							
								
								
									
										33
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										33
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							| @@ -16,31 +16,10 @@ | ||||
| #   public *; | ||||
| #} | ||||
|  | ||||
| # Gson uses generic type information stored in a class file when working with fields. Proguard | ||||
| # removes such information by default, so configure it to keep all of it. | ||||
| -keepattributes Signature | ||||
|  | ||||
| # For using GSON @Expose annotation | ||||
| -keepattributes *Annotation* | ||||
|  | ||||
| # Gson specific classes | ||||
| -keep class sun.misc.Unsafe { *; } | ||||
| -keep class com.google.gson.** { *; } | ||||
|  | ||||
| # Application classes that will be serialized/deserialized over Gson | ||||
| -keep class com.topjohnwu.magisk.module.** { *; } | ||||
| -keep class com.topjohnwu.magisk.utils.ModuleHelper$ValueSortedMap { *; } | ||||
|  | ||||
| # Prevent proguard from stripping interface information from TypeAdapterFactory, | ||||
| # JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter) | ||||
| -keep class * implements com.google.gson.TypeAdapterFactory | ||||
| -keep class * implements com.google.gson.JsonSerializer | ||||
| -keep class * implements com.google.gson.JsonDeserializer | ||||
|  | ||||
| -keep class android.support.v7.internal.** { *; } | ||||
| -keep interface android.support.v7.internal.** { *; } | ||||
| -keep class android.support.v7.** { *; } | ||||
| -keep interface android.support.v7.** { *; } | ||||
|  | ||||
| # SpongyCastle | ||||
| -keep class org.spongycastle.** {*;} | ||||
| -keep class org.spongycastle.** { *; } | ||||
| -dontwarn javax.naming.** | ||||
|  | ||||
| -dontwarn android.content.** | ||||
| -dontwarn android.animation.** | ||||
|  | ||||
|   | ||||
| @@ -1,18 +1,18 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <manifest package="com.topjohnwu.magisk" | ||||
|           xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|  | ||||
|           xmlns:tools="http://schemas.android.com/tools"> | ||||
| <manifest | ||||
|     package="com.topjohnwu.magisk" | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools"> | ||||
|  | ||||
|     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> | ||||
|     <uses-permission android:name="android.permission.INTERNET" /> | ||||
|     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> | ||||
|     <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> | ||||
|     <uses-permission | ||||
|         android:name="android.permission.PACKAGE_USAGE_STATS" | ||||
|         tools:ignore="ProtectedPermissions" /> | ||||
|     <uses-permission android:name="android.permission.VIBRATE" /> | ||||
|     <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" /> | ||||
|  | ||||
|     <application | ||||
|         android:name=".MagiskManager" | ||||
|         android:allowBackup="true" | ||||
|         android:icon="@mipmap/ic_launcher" | ||||
|         android:label="@string/app_name" | ||||
| @@ -41,6 +41,42 @@ | ||||
|         <activity | ||||
|             android:name=".SettingsActivity" | ||||
|             android:theme="@style/AppTheme.Transparent" /> | ||||
|         <activity | ||||
|             android:name=".superuser.RequestActivity" | ||||
|             android:excludeFromRecents="true" | ||||
|             android:launchMode="singleTask" | ||||
|             android:taskAffinity="internal.superuser" | ||||
|             android:theme="@android:style/Theme.NoDisplay" /> | ||||
|         <activity | ||||
|             android:name=".superuser.SuRequestActivity" | ||||
|             android:excludeFromRecents="true" | ||||
|             android:taskAffinity="internal.superuser" | ||||
|             android:theme="@style/SuRequest" /> | ||||
|  | ||||
|         <receiver android:name=".superuser.SuReceiver" /> | ||||
|  | ||||
|         <receiver android:name=".receivers.BootReceiver"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.BOOT_COMPLETED" /> | ||||
|             </intent-filter> | ||||
|         </receiver> | ||||
|  | ||||
|         <receiver android:name=".receivers.PackageReceiver"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.PACKAGE_REPLACED" /> | ||||
|                 <action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" /> | ||||
|                 <data android:scheme="package" /> | ||||
|             </intent-filter> | ||||
|         </receiver> | ||||
|  | ||||
|         <receiver android:name=".receivers.ManagerUpdate" /> | ||||
|  | ||||
|         <service android:name=".services.OnBootIntentService" /> | ||||
|  | ||||
|         <service | ||||
|             android:name=".services.UpdateCheckService" | ||||
|             android:permission="android.permission.BIND_JOB_SERVICE" | ||||
|             android:exported="true" /> | ||||
|  | ||||
|         <provider | ||||
|             android:name="android.support.v4.content.FileProvider" | ||||
|   | ||||
							
								
								
									
										276
									
								
								app/src/main/assets/dark.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								app/src/main/assets/dark.css
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										277
									
								
								app/src/main/assets/light.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								app/src/main/assets/light.css
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										137
									
								
								app/src/main/assets/magisk_uninstaller.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								app/src/main/assets/magisk_uninstaller.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| #!/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 | ||||
							
								
								
									
										192
									
								
								app/src/main/assets/util_functions.sh
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								app/src/main/assets/util_functions.sh
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,192 @@ | ||||
| ########################################################################################## | ||||
| #  | ||||
| # 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: 42 KiB After Width: | Height: | Size: 73 KiB | 
| @@ -3,21 +3,20 @@ package com.topjohnwu.magisk; | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v7.app.ActionBar; | ||||
| import android.support.v7.app.AlertDialog; | ||||
| import android.support.v7.app.AppCompatActivity; | ||||
| import android.support.v7.widget.Toolbar; | ||||
| import android.text.Html; | ||||
| import android.text.Spanned; | ||||
| import android.text.TextUtils; | ||||
| import android.text.method.LinkMovementMethod; | ||||
| import android.view.View; | ||||
| import android.view.WindowManager; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| 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; | ||||
| @@ -25,11 +24,12 @@ import java.io.InputStream; | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
|  | ||||
| public class AboutActivity extends AppCompatActivity { | ||||
| public class AboutActivity extends Activity { | ||||
|  | ||||
|     private static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/MagiskManager"; | ||||
|     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 DONATION_URL = "http://topjohnwu.github.io/donate"; | ||||
|     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; | ||||
| @@ -38,15 +38,12 @@ public class AboutActivity extends AppCompatActivity { | ||||
|     @BindView(R.id.app_source_code) AboutCardRow appSourceCode; | ||||
|     @BindView(R.id.support_thread) AboutCardRow supportThread; | ||||
|     @BindView(R.id.donation) AboutCardRow donation; | ||||
|     private AlertDialog.Builder builder; | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         String theme = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("theme", ""); | ||||
|         Logger.dev("AboutActivity: Theme is " + theme); | ||||
|         if (theme.equals("Dark")) { | ||||
|             setTheme(R.style.AppTheme_dh); | ||||
|         if (getApplicationContext().isDarkTheme) { | ||||
|             setTheme(R.style.AppTheme_Transparent_Dark); | ||||
|         } | ||||
|         setContentView(R.layout.activity_about); | ||||
|         ButterKnife.bind(this); | ||||
| @@ -63,24 +60,17 @@ public class AboutActivity extends AppCompatActivity { | ||||
|         appVersionInfo.setSummary(BuildConfig.VERSION_NAME); | ||||
|  | ||||
|         String changes = null; | ||||
|         try { | ||||
|             InputStream is = getAssets().open("changelog.html"); | ||||
|         try (InputStream is = getAssets().open("changelog.html")) { | ||||
|             int size = is.available(); | ||||
|  | ||||
|             byte[] buffer = new byte[size]; | ||||
|             is.read(buffer); | ||||
|             is.close(); | ||||
|  | ||||
|             changes = new String(buffer); | ||||
|         } catch (IOException ignored) { | ||||
|         } | ||||
|  | ||||
|         appChangelog.removeSummary(); | ||||
|         if (theme.equals("Dark")) { | ||||
|             builder = new AlertDialog.Builder(this, R.style.AlertDialog_dh); | ||||
|         } else { | ||||
|             builder = new AlertDialog.Builder(this); | ||||
|         } | ||||
|         if (changes == null) { | ||||
|             appChangelog.setVisibility(View.GONE); | ||||
|         } else { | ||||
| @@ -91,13 +81,11 @@ public class AboutActivity extends AppCompatActivity { | ||||
|                 result = Html.fromHtml(changes); | ||||
|             } | ||||
|             appChangelog.setOnClickListener(v -> { | ||||
|                 AlertDialog d = builder | ||||
|                 AlertDialog d = new AlertDialogBuilder(this) | ||||
|                         .setTitle(R.string.app_changelog) | ||||
|                         .setMessage(result) | ||||
|                         .setPositiveButton(android.R.string.ok, null) | ||||
|                         .create(); | ||||
|  | ||||
|                 d.show(); | ||||
|                         .show(); | ||||
|  | ||||
|                 //noinspection ConstantConditions | ||||
|                 ((TextView) d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance()); | ||||
| @@ -112,7 +100,7 @@ public class AboutActivity extends AppCompatActivity { | ||||
|             } else { | ||||
|                 result = Html.fromHtml(getString(R.string.app_developers_)); | ||||
|             } | ||||
|             AlertDialog d = builder | ||||
|             AlertDialog d = new AlertDialogBuilder(this) | ||||
|                     .setTitle(R.string.app_developers) | ||||
|                     .setMessage(result) | ||||
|                     .setPositiveButton(android.R.string.ok, null) | ||||
| @@ -142,18 +130,4 @@ public class AboutActivity extends AppCompatActivity { | ||||
|         setFloating(); | ||||
|     } | ||||
|  | ||||
|     public void setFloating() { | ||||
|         boolean isTablet = getResources().getBoolean(R.bool.isTablet); | ||||
|         if (isTablet) { | ||||
|             WindowManager.LayoutParams params = getWindow().getAttributes(); | ||||
|             params.height = getResources().getDimensionPixelSize(R.dimen.floating_height); | ||||
|             params.width = getResources().getDimensionPixelSize(R.dimen.floating_width); | ||||
|             params.alpha = 1.0f; | ||||
|             params.dimAmount = 0.6f; | ||||
|             params.flags |= 2; | ||||
|             getWindow().setAttributes(params); | ||||
|             setFinishOnTouchOutside(true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
|   | ||||
| @@ -1,113 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.support.design.widget.CoordinatorLayout; | ||||
| import android.support.design.widget.Snackbar; | ||||
| import android.support.v4.view.ViewCompat; | ||||
| import android.support.v4.view.ViewPropertyAnimatorCompat; | ||||
| import android.util.AttributeSet; | ||||
| import android.view.View; | ||||
|  | ||||
| import com.github.clans.fab.FloatingActionMenu; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| /** | ||||
|  * Created by Matteo on 08/08/2015. | ||||
|  * | ||||
|  * Floating Action Menu Behavior for Clans.FloatingActionButton | ||||
|  * https://github.com/Clans/FloatingActionButton/ | ||||
|  * | ||||
|  * Use this behavior as your app:layout_behavior attribute in your Floating Action Menu to use the | ||||
|  * FabMenu in a Coordinator Layout. | ||||
|  * | ||||
|  * Remember to use the correct namespace for the fab: | ||||
|  * xmlns:fab="http://schemas.android.com/apk/res-auto" | ||||
|  */ | ||||
| public class FABBehavior extends CoordinatorLayout.Behavior { | ||||
|     private float mTranslationY; | ||||
|  | ||||
|     public FABBehavior(Context context, AttributeSet attrs) { | ||||
|         super(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) { | ||||
|         return dependency instanceof Snackbar.SnackbarLayout; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) { | ||||
|         if (dependency instanceof Snackbar.SnackbarLayout) { | ||||
|             updateTranslation(parent, child); | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDependentViewRemoved(CoordinatorLayout parent, View child, View dependency) { | ||||
|         if (dependency instanceof Snackbar.SnackbarLayout) { | ||||
|             revertTranslation(child); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void updateTranslation(CoordinatorLayout parent, View child) { | ||||
|         float translationY = getTranslationY(parent, child); | ||||
|         if (translationY != mTranslationY) { | ||||
|             ViewPropertyAnimatorCompat anim = ViewCompat.animate(child); | ||||
|             anim.cancel(); | ||||
|             anim.translationY(translationY).setDuration(100); | ||||
|             mTranslationY = translationY; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void revertTranslation(View child) { | ||||
|         if (mTranslationY != 0) { | ||||
|             ViewPropertyAnimatorCompat anim = ViewCompat.animate(child); | ||||
|             anim.cancel(); | ||||
|             anim.translationY(0).setDuration(100); | ||||
|             mTranslationY = 0; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private float getTranslationY(CoordinatorLayout parent, View child) { | ||||
|         float minOffset = 0.0F; | ||||
|         List dependencies = parent.getDependencies(child); | ||||
|         int i = 0; | ||||
|  | ||||
|         for (int z = dependencies.size(); i < z; ++i) { | ||||
|             View view = (View) dependencies.get(i); | ||||
|             if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(child, view)) { | ||||
|                 minOffset = Math.min(minOffset, ViewCompat.getTranslationY(view) - (float) view.getHeight()); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return minOffset; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * onStartNestedScroll and onNestedScroll will hide/show the FabMenu when a scroll is detected. | ||||
|      */ | ||||
|     @Override | ||||
|     public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, | ||||
|                                        View directTargetChild, View target, int nestedScrollAxes) { | ||||
|         return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL || | ||||
|                 super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, | ||||
|                         nestedScrollAxes); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onNestedScroll(CoordinatorLayout coordinatorLayout, View child, View target, | ||||
|                                int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { | ||||
|         super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, | ||||
|                 dyUnconsumed); | ||||
|         FloatingActionMenu fabMenu = (FloatingActionMenu) child; | ||||
|         if (dyConsumed > 0 && !fabMenu.isMenuButtonHidden()) { | ||||
|             fabMenu.hideMenuButton(true); | ||||
|         } else if (dyConsumed < 0 && fabMenu.isMenuButtonHidden()) { | ||||
|             fabMenu.showMenuButton(true); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,105 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.app.Fragment; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v7.widget.CardView; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.ArrayAdapter; | ||||
| import android.widget.Button; | ||||
| import android.widget.CheckBox; | ||||
| import android.widget.Spinner; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.receivers.MagiskDlReceiver; | ||||
| import com.topjohnwu.magisk.utils.CallbackHandler; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
|  | ||||
| public class InstallFragment extends Fragment implements CallbackHandler.EventListener { | ||||
|  | ||||
|     public static final CallbackHandler.Event blockDetectionDone = new CallbackHandler.Event(); | ||||
|  | ||||
|     public static List<String> blockList; | ||||
|     public static String bootBlock = null; | ||||
|  | ||||
|     @BindView(R.id.install_title) TextView installTitle; | ||||
|     @BindView(R.id.block_spinner) Spinner spinner; | ||||
|     @BindView(R.id.detect_bootimage) Button detectButton; | ||||
|     @BindView(R.id.flash_button) CardView flashButton; | ||||
|     @BindView(R.id.keep_force_enc) CheckBox keepEncChkbox; | ||||
|     @BindView(R.id.keep_verity) CheckBox keepVerityChkbox; | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||
|         View v = inflater.inflate(R.layout.install_fragment, container, false); | ||||
|         ButterKnife.bind(this, v); | ||||
|         detectButton.setOnClickListener(v1 -> toAutoDetect()); | ||||
|         installTitle.setText(getString(R.string.install_magisk_title, StatusFragment.remoteMagiskVersion)); | ||||
|         flashButton.setOnClickListener(v1 -> { | ||||
|             String bootImage = bootBlock; | ||||
|             if (bootImage == null) { | ||||
|                 bootImage = blockList.get(spinner.getSelectedItemPosition() - 1); | ||||
|             } | ||||
|             String filename = "Magisk-v" + String.valueOf(StatusFragment.remoteMagiskVersion) + ".zip"; | ||||
|             String finalBootImage = bootImage; | ||||
|             MainActivity.alertBuilder | ||||
|                     .setTitle(getString(R.string.repo_install_title, getString(R.string.magisk))) | ||||
|                     .setMessage(getString(R.string.repo_install_msg, filename)) | ||||
|                     .setCancelable(true) | ||||
|                     .setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive( | ||||
|                             getActivity(), | ||||
|                             new MagiskDlReceiver(finalBootImage, keepEncChkbox.isChecked(), keepVerityChkbox.isChecked()), | ||||
|                             StatusFragment.magiskLink, | ||||
|                             Utils.getLegalFilename(filename))) | ||||
|                     .setNegativeButton(R.string.no_thanks, null) | ||||
|                     .show(); | ||||
|         }); | ||||
|         if (blockDetectionDone.isTriggered) { | ||||
|             updateUI(); | ||||
|         } | ||||
|         return v; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onTrigger(CallbackHandler.Event event) { | ||||
|         updateUI(); | ||||
|     } | ||||
|  | ||||
|     private void updateUI() { | ||||
|         List<String> items = new ArrayList<>(blockList); | ||||
|         items.add(0, getString(R.string.auto_detect, bootBlock)); | ||||
|         ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(), | ||||
|                 android.R.layout.simple_spinner_item, items); | ||||
|         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); | ||||
|         spinner.setAdapter(adapter); | ||||
|         toAutoDetect(); | ||||
|     } | ||||
|  | ||||
|     private void toAutoDetect() { | ||||
|         if (bootBlock != null) { | ||||
|             spinner.setSelection(0); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
|         getActivity().setTitle(R.string.install); | ||||
|         CallbackHandler.register(blockDetectionDone, this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         CallbackHandler.unRegister(blockDetectionDone, this); | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										510
									
								
								app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										510
									
								
								app/src/main/java/com/topjohnwu/magisk/MagiskFragment.java
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,13 +1,12 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.app.Fragment; | ||||
| import android.content.pm.ApplicationInfo; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v4.view.MenuItemCompat; | ||||
| import android.support.v4.widget.SwipeRefreshLayout; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.text.TextUtils; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuInflater; | ||||
| @@ -16,60 +15,67 @@ import android.view.ViewGroup; | ||||
| import android.widget.SearchView; | ||||
|  | ||||
| import com.topjohnwu.magisk.adapters.ApplicationAdapter; | ||||
| import com.topjohnwu.magisk.utils.Async; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import com.topjohnwu.magisk.asyncs.MagiskHide; | ||||
| import com.topjohnwu.magisk.components.Fragment; | ||||
| import com.topjohnwu.magisk.utils.CallbackEvent; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import butterknife.Unbinder; | ||||
|  | ||||
| public class MagiskHideFragment extends Fragment { | ||||
| public class MagiskHideFragment extends Fragment implements CallbackEvent.Listener<Void> { | ||||
|  | ||||
|     private Unbinder unbinder; | ||||
|     @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; | ||||
|     @BindView(R.id.recyclerView) RecyclerView recyclerView; | ||||
|  | ||||
|     private PackageManager packageManager; | ||||
|     private View mView; | ||||
|     private List<ApplicationInfo> listApps = new ArrayList<>(), fListApps = new ArrayList<>(); | ||||
|     private List<String> hideList = new ArrayList<>(); | ||||
|     private ApplicationAdapter appAdapter = new ApplicationAdapter(fListApps, hideList); | ||||
|     private ApplicationAdapter appAdapter; | ||||
|  | ||||
|     private SearchView.OnQueryTextListener searchListener; | ||||
|     private String lastFilter; | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setHasOptionsMenu(true); | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { | ||||
|         mView = inflater.inflate(R.layout.magisk_hide_fragment, container, false); | ||||
|         ButterKnife.bind(this, mView); | ||||
|         View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false); | ||||
|         unbinder = ButterKnife.bind(this, view); | ||||
|  | ||||
|         packageManager = getActivity().getPackageManager(); | ||||
|         PackageManager packageManager = getActivity().getPackageManager(); | ||||
|  | ||||
|         mSwipeRefreshLayout.setRefreshing(true); | ||||
|         mSwipeRefreshLayout.setOnRefreshListener(() -> { | ||||
|             recyclerView.setVisibility(View.GONE); | ||||
|             new LoadApps().exec(); | ||||
|         }); | ||||
|         mSwipeRefreshLayout.setOnRefreshListener(() -> new MagiskHide(getActivity()).list()); | ||||
|  | ||||
|         appAdapter = new ApplicationAdapter(packageManager); | ||||
|         recyclerView.setAdapter(appAdapter); | ||||
|  | ||||
|         searchListener = new SearchView.OnQueryTextListener() { | ||||
|             @Override | ||||
|             public boolean onQueryTextSubmit(String query) { | ||||
|                 lastFilter = query; | ||||
|                 appAdapter.filter(query); | ||||
|                 return false; | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public boolean onQueryTextChange(String newText) { | ||||
|                 new FilterApps().exec(newText); | ||||
|                 lastFilter = newText; | ||||
|                 appAdapter.filter(newText); | ||||
|                 return false; | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         new LoadApps().exec(); | ||||
|         return mView; | ||||
|         if (getApplication().magiskHideDone.isTriggered) { | ||||
|             onTrigger(getApplication().magiskHideDone); | ||||
|         } | ||||
|  | ||||
|         return view; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -80,57 +86,31 @@ public class MagiskHideFragment extends Fragment { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
|         setHasOptionsMenu(true); | ||||
|         mView = this.getView(); | ||||
|     public void onStart() { | ||||
|         super.onStart(); | ||||
|         getActivity().setTitle(R.string.magiskhide); | ||||
|         getApplication().magiskHideDone.register(this); | ||||
|     } | ||||
|  | ||||
|     private class LoadApps extends Async.RootTask<Void, Void, Void> { | ||||
|         @Override | ||||
|         protected Void doInBackground(Void... voids) { | ||||
|             listApps.clear(); | ||||
|             hideList.clear(); | ||||
|             fListApps.clear(); | ||||
|             listApps.addAll(packageManager.getInstalledApplications(PackageManager.GET_META_DATA)); | ||||
|             Collections.sort(listApps, (a, b) -> a.loadLabel(packageManager).toString().toLowerCase() | ||||
|                     .compareTo(b.loadLabel(packageManager).toString().toLowerCase())); | ||||
|             hideList.addAll(Shell.su(Async.MAGISK_HIDE_PATH + "list")); | ||||
|             fListApps.addAll(listApps); | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         protected void onPostExecute(Void aVoid) { | ||||
|             updateUI(); | ||||
|         } | ||||
|     @Override | ||||
|     public void onStop() { | ||||
|         getApplication().magiskHideDone.unRegister(this); | ||||
|         super.onStop(); | ||||
|     } | ||||
|  | ||||
|     private class FilterApps extends Async.NormalTask<String, Void, Void> { | ||||
|         @Override | ||||
|         protected Void doInBackground(String... strings) { | ||||
|             String newText = strings[0]; | ||||
|             fListApps.clear(); | ||||
|             for (ApplicationInfo info : listApps) { | ||||
|                 if (info.loadLabel(packageManager).toString().toLowerCase().contains(newText.toLowerCase()) | ||||
|                         || info.packageName.toLowerCase().contains(newText.toLowerCase())) { | ||||
|                     fListApps.add(info); | ||||
|                 } | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         protected void onPostExecute(Void v) { | ||||
|             appAdapter.notifyDataSetChanged(); | ||||
|         } | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         super.onDestroyView(); | ||||
|         unbinder.unbind(); | ||||
|     } | ||||
|  | ||||
|     private void updateUI() { | ||||
|         appAdapter.notifyDataSetChanged(); | ||||
|         recyclerView.setVisibility(View.VISIBLE); | ||||
|     @Override | ||||
|     public void onTrigger(CallbackEvent<Void> event) { | ||||
|         Logger.dev("MagiskHideFragment: UI refresh"); | ||||
|         appAdapter.setLists(getApplication().appList, getApplication().magiskHideList); | ||||
|         mSwipeRefreshLayout.setRefreshing(false); | ||||
|         if (!TextUtils.isEmpty(lastFilter)) { | ||||
|             appAdapter.filter(lastFilter); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
| } | ||||
|   | ||||
							
								
								
									
										241
									
								
								app/src/main/java/com/topjohnwu/magisk/MagiskLogFragment.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								app/src/main/java/com/topjohnwu/magisk/MagiskLogFragment.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,241 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.Manifest; | ||||
| import android.annotation.SuppressLint; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.os.Build; | ||||
| import android.os.Bundle; | ||||
| import android.os.Environment; | ||||
| import android.os.Handler; | ||||
| import android.support.annotation.NonNull; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.design.widget.Snackbar; | ||||
| import android.support.v4.app.ActivityCompat; | ||||
| import android.text.TextUtils; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuInflater; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.HorizontalScrollView; | ||||
| import android.widget.ProgressBar; | ||||
| import android.widget.ScrollView; | ||||
| import android.widget.TextView; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import com.topjohnwu.magisk.asyncs.RootTask; | ||||
| import com.topjohnwu.magisk.components.Fragment; | ||||
| import com.topjohnwu.magisk.components.SnackbarMaker; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| import java.io.File; | ||||
| import java.io.FileWriter; | ||||
| import java.io.IOException; | ||||
| import java.util.Calendar; | ||||
| import java.util.List; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import butterknife.Unbinder; | ||||
|  | ||||
| public class MagiskLogFragment extends Fragment { | ||||
|  | ||||
|     private static final String MAGISK_LOG = "/cache/magisk.log"; | ||||
|  | ||||
|     private Unbinder unbinder; | ||||
|     @BindView(R.id.txtLog) TextView txtLog; | ||||
|     @BindView(R.id.svLog) ScrollView svLog; | ||||
|     @BindView(R.id.hsvLog) HorizontalScrollView hsvLog; | ||||
|  | ||||
|     @BindView(R.id.progressBar) ProgressBar progressBar; | ||||
|  | ||||
|     private MenuItem mClickedMenuItem; | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setHasOptionsMenu(true); | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { | ||||
|         View view = inflater.inflate(R.layout.fragment_magisk_log, container, false); | ||||
|         unbinder = ButterKnife.bind(this, view); | ||||
|  | ||||
|         txtLog.setTextIsSelectable(true); | ||||
|  | ||||
|         new LogManager().read(); | ||||
|  | ||||
|         return view; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onStart() { | ||||
|         super.onStart(); | ||||
|         getActivity().setTitle(R.string.log); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
|         new LogManager().read(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         super.onDestroyView(); | ||||
|         unbinder.unbind(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { | ||||
|         inflater.inflate(R.menu.menu_log, menu); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         mClickedMenuItem = item; | ||||
|         switch (item.getItemId()) { | ||||
|             case R.id.menu_refresh: | ||||
|                 new LogManager().read(); | ||||
|                 return true; | ||||
|             case R.id.menu_save: | ||||
|                 new LogManager().save(); | ||||
|                 return true; | ||||
|             case R.id.menu_clear: | ||||
|                 new LogManager().clear(); | ||||
|                 return true; | ||||
|             default: | ||||
|                 return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | ||||
|         super.onRequestPermissionsResult(requestCode, permissions, grantResults); | ||||
|         if (requestCode == 0) { | ||||
|             if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { | ||||
|                 if (mClickedMenuItem != null) { | ||||
|                     new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500); | ||||
|                 } | ||||
|             } else { | ||||
|                 SnackbarMaker.make(txtLog, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private class LogManager extends RootTask<Object, Void, Object> { | ||||
|  | ||||
|         int mode; | ||||
|         File targetFile; | ||||
|  | ||||
|         @SuppressLint("DefaultLocale") | ||||
|         @Override | ||||
|         protected Object doInRoot(Object... params) { | ||||
|             mode = (int) params[0]; | ||||
|             switch (mode) { | ||||
|                 case 0: | ||||
|                     List<String> logList = Utils.readFile(MAGISK_LOG); | ||||
|  | ||||
|                     if (Utils.isValidShellResponse(logList)) { | ||||
|                         StringBuilder llog = new StringBuilder(15 * 10 * 1024); | ||||
|                         for (String s : logList) { | ||||
|                             llog.append(s).append("\n"); | ||||
|                         } | ||||
|                         return llog.toString(); | ||||
|                     } | ||||
|                     return ""; | ||||
|  | ||||
|                 case 1: | ||||
|                     Shell.su("echo > " + MAGISK_LOG); | ||||
|                     SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show(); | ||||
|                     return ""; | ||||
|  | ||||
|                 case 2: | ||||
|                     if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { | ||||
|                         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { | ||||
|                             requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0); | ||||
|                         } | ||||
|                         return false; | ||||
|                     } | ||||
|  | ||||
|                     if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { | ||||
|                         return false; | ||||
|                     } | ||||
|  | ||||
|                     Calendar now = Calendar.getInstance(); | ||||
|                     String filename = String.format( | ||||
|                             "magisk_%s_%04d%02d%02d_%02d%02d%02d.log", "error", | ||||
|                             now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1, | ||||
|                             now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY), | ||||
|                             now.get(Calendar.MINUTE), now.get(Calendar.SECOND)); | ||||
|  | ||||
|                     targetFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/MagiskManager/" + filename); | ||||
|  | ||||
|                     if ((!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs()) | ||||
|                             || (targetFile.exists() && !targetFile.delete())) { | ||||
|                         return false; | ||||
|                     } | ||||
|  | ||||
|                     List<String> in = Utils.readFile(MAGISK_LOG); | ||||
|  | ||||
|                     if (Utils.isValidShellResponse(in)) { | ||||
|                         try (FileWriter out = new FileWriter(targetFile)) { | ||||
|                             for (String line : in) | ||||
|                                 out.write(line + "\n"); | ||||
|                             return true; | ||||
|                         } catch (IOException e) { | ||||
|                             e.printStackTrace(); | ||||
|                             return false; | ||||
|                         } | ||||
|                     } | ||||
|                     return false; | ||||
|             } | ||||
|             return null; | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         protected void onPostExecute(Object o) { | ||||
|             if (o == null) return; | ||||
|             boolean bool; | ||||
|             String llog; | ||||
|             switch (mode) { | ||||
|                 case 0: | ||||
|                 case 1: | ||||
|                     llog = (String) o; | ||||
|                     progressBar.setVisibility(View.GONE); | ||||
|                     if (TextUtils.isEmpty(llog)) | ||||
|                         txtLog.setText(R.string.log_is_empty); | ||||
|                     else | ||||
|                         txtLog.setText(llog); | ||||
|                     svLog.post(() -> svLog.scrollTo(0, txtLog.getHeight())); | ||||
|                     hsvLog.post(() -> hsvLog.scrollTo(0, 0)); | ||||
|                     break; | ||||
|                 case 2: | ||||
|                     bool = (boolean) o; | ||||
|                     if (bool) { | ||||
|                         Toast.makeText(getActivity(), targetFile.toString(), Toast.LENGTH_LONG).show(); | ||||
|                     } else { | ||||
|                         Toast.makeText(getActivity(), getString(R.string.logs_save_failed), Toast.LENGTH_LONG).show(); | ||||
|                     } | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         public void read() { | ||||
|             exec(0); | ||||
|         } | ||||
|  | ||||
|         public void clear() { | ||||
|             exec(1); | ||||
|         } | ||||
|  | ||||
|         public void save() { | ||||
|             exec(2); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| } | ||||
							
								
								
									
										220
									
								
								app/src/main/java/com/topjohnwu/magisk/MagiskManager.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										220
									
								
								app/src/main/java/com/topjohnwu/magisk/MagiskManager.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,220 @@ | ||||
| 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> blockDetectionDone = new CallbackEvent<>(); | ||||
|     public final CallbackEvent<Void> magiskHideDone = new CallbackEvent<>(); | ||||
|     public final CallbackEvent<Void> reloadMainActivity = new CallbackEvent<>(); | ||||
|     public final CallbackEvent<Void> moduleLoadDone = new CallbackEvent<>(); | ||||
|     public final CallbackEvent<Void> repoLoadDone = new CallbackEvent<>(); | ||||
|     public final CallbackEvent<Void> updateCheckDone = new CallbackEvent<>(); | ||||
|     public final CallbackEvent<Void> safetyNetDone = new CallbackEvent<>(); | ||||
|  | ||||
|     // 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; | ||||
|  | ||||
|     private static Handler mHandler = new Handler(); | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate() { | ||||
|         super.onCreate(); | ||||
|         prefs = PreferenceManager.getDefaultSharedPreferences(this); | ||||
|     } | ||||
|  | ||||
|     public void toast(String msg, int duration) { | ||||
|         mHandler.post(() -> Toast.makeText(this, msg, duration).show()); | ||||
|     } | ||||
|  | ||||
|     public void toast(int resId, int duration) { | ||||
|         mHandler.post(() -> Toast.makeText(this, resId, duration).show()); | ||||
|     } | ||||
|  | ||||
|     public void init() { | ||||
|         isDarkTheme = prefs.getBoolean("dark_theme", false); | ||||
|         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(); | ||||
|         // Always start a new root shell manually, just for safety | ||||
|         Shell.init(); | ||||
|         updateMagiskInfo(); | ||||
|         // 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( | ||||
|                     "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(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("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() { | ||||
|         // Create the app data directory, so su binary can work properly | ||||
|         new File(getApplicationInfo().dataDir).mkdirs(); | ||||
|  | ||||
|         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; | ||||
|         } | ||||
|          | ||||
|     } | ||||
|  | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,11 +1,11 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.app.Fragment; | ||||
| import android.content.Intent; | ||||
| import android.net.Uri; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.design.widget.FloatingActionButton; | ||||
| import android.support.v4.widget.SwipeRefreshLayout; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.view.LayoutInflater; | ||||
| @@ -13,39 +13,38 @@ import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.github.clans.fab.FloatingActionButton; | ||||
| import com.topjohnwu.magisk.adapters.ModulesAdapter; | ||||
| import com.topjohnwu.magisk.asyncs.FlashZip; | ||||
| import com.topjohnwu.magisk.asyncs.LoadModules; | ||||
| import com.topjohnwu.magisk.components.Fragment; | ||||
| import com.topjohnwu.magisk.module.Module; | ||||
| import com.topjohnwu.magisk.utils.Async; | ||||
| import com.topjohnwu.magisk.utils.CallbackHandler; | ||||
| import com.topjohnwu.magisk.utils.CallbackEvent; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.ModuleHelper; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import butterknife.Unbinder; | ||||
|  | ||||
| public class ModulesFragment extends Fragment implements CallbackHandler.EventListener { | ||||
|  | ||||
|     public static final CallbackHandler.Event moduleLoadDone = new CallbackHandler.Event(); | ||||
| public class ModulesFragment extends Fragment implements CallbackEvent.Listener<Void> { | ||||
|  | ||||
|     private static final int FETCH_ZIP_CODE = 2; | ||||
|  | ||||
|     private Unbinder unbinder; | ||||
|     @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; | ||||
|     @BindView(R.id.recyclerView) RecyclerView recyclerView; | ||||
|     @BindView(R.id.empty_rv) TextView emptyTv; | ||||
|     @BindView(R.id.empty_rv) TextView emptyRv; | ||||
|     @BindView(R.id.fab) FloatingActionButton fabio; | ||||
|  | ||||
|     private List<Module> listModules = new ArrayList<>(); | ||||
|     private View mView; | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { | ||||
|         mView = inflater.inflate(R.layout.modules_fragment, container, false); | ||||
|         ButterKnife.bind(this, mView); | ||||
|         View view = inflater.inflate(R.layout.fragment_modules, container, false); | ||||
|         unbinder = ButterKnife.bind(this, view); | ||||
|  | ||||
|         fabio.setOnClickListener(v -> { | ||||
|             Intent intent = new Intent(Intent.ACTION_GET_CONTENT); | ||||
| @@ -55,7 +54,7 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi | ||||
|  | ||||
|         mSwipeRefreshLayout.setOnRefreshListener(() -> { | ||||
|             recyclerView.setVisibility(View.GONE); | ||||
|             new Async.LoadModules().exec(); | ||||
|             new LoadModules(getActivity()).exec(); | ||||
|         }); | ||||
|  | ||||
|         recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { | ||||
| @@ -70,15 +69,15 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         if (moduleLoadDone.isTriggered) { | ||||
|         if (getApplication().moduleLoadDone.isTriggered) { | ||||
|             updateUI(); | ||||
|         } | ||||
|  | ||||
|         return mView; | ||||
|         return view; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onTrigger(CallbackHandler.Event event) { | ||||
|     public void onTrigger(CallbackEvent<Void> event) { | ||||
|         Logger.dev("ModulesFragment: UI refresh triggered"); | ||||
|         updateUI(); | ||||
|     } | ||||
| @@ -88,32 +87,38 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi | ||||
|         if (requestCode == FETCH_ZIP_CODE && resultCode == Activity.RESULT_OK && data != null) { | ||||
|             // Get the URI of the selected file | ||||
|             final Uri uri = data.getData(); | ||||
|             new Async.FlashZIP(getActivity(), uri).exec(); | ||||
|             new FlashZip(getActivity(), uri).exec(); | ||||
|         } | ||||
|  | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
|         mView = this.getView(); | ||||
|         CallbackHandler.register(moduleLoadDone, this); | ||||
|     public void onStart() { | ||||
|         super.onStart(); | ||||
|         getApplication().moduleLoadDone.register(this); | ||||
|         getActivity().setTitle(R.string.modules); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         CallbackHandler.unRegister(moduleLoadDone, this); | ||||
|     public void onStop() { | ||||
|         getApplication().moduleLoadDone.unRegister(this); | ||||
|         super.onStop(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         super.onDestroyView(); | ||||
|         unbinder.unbind(); | ||||
|     } | ||||
|  | ||||
|     private void updateUI() { | ||||
|         ModuleHelper.getModuleList(listModules); | ||||
|         listModules.clear(); | ||||
|         listModules.addAll(getApplication().moduleMap.values()); | ||||
|         if (listModules.size() == 0) { | ||||
|             emptyTv.setVisibility(View.VISIBLE); | ||||
|             emptyRv.setVisibility(View.VISIBLE); | ||||
|             recyclerView.setVisibility(View.GONE); | ||||
|         } else { | ||||
|             emptyTv.setVisibility(View.GONE); | ||||
|             emptyRv.setVisibility(View.GONE); | ||||
|             recyclerView.setVisibility(View.VISIBLE); | ||||
|             recyclerView.setAdapter(new ModulesAdapter(listModules)); | ||||
|         } | ||||
|   | ||||
| @@ -1,6 +1,5 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.app.Fragment; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v4.view.MenuItemCompat; | ||||
| @@ -16,24 +15,26 @@ import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.adapters.ReposAdapter; | ||||
| import com.topjohnwu.magisk.adapters.SimpleSectionedRecyclerViewAdapter; | ||||
| import com.topjohnwu.magisk.asyncs.LoadRepos; | ||||
| import com.topjohnwu.magisk.asyncs.ParallelTask; | ||||
| import com.topjohnwu.magisk.components.Fragment; | ||||
| import com.topjohnwu.magisk.module.Module; | ||||
| import com.topjohnwu.magisk.module.Repo; | ||||
| import com.topjohnwu.magisk.utils.Async; | ||||
| import com.topjohnwu.magisk.utils.CallbackHandler; | ||||
| import com.topjohnwu.magisk.utils.CallbackEvent; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.ModuleHelper; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import butterknife.Unbinder; | ||||
|  | ||||
| public class ReposFragment extends Fragment implements CallbackHandler.EventListener { | ||||
|  | ||||
|     public static final CallbackHandler.Event repoLoadDone = new CallbackHandler.Event(); | ||||
| public class ReposFragment extends Fragment implements CallbackEvent.Listener<Void> { | ||||
|  | ||||
|     private Unbinder unbinder; | ||||
|     @BindView(R.id.recyclerView) RecyclerView recyclerView; | ||||
|     @BindView(R.id.empty_rv) TextView emptyTv; | ||||
|     @BindView(R.id.empty_rv) TextView emptyRv; | ||||
|     @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; | ||||
|  | ||||
|     private List<Repo> mUpdateRepos = new ArrayList<>(); | ||||
| @@ -47,16 +48,19 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList | ||||
|  | ||||
|     private SearchView.OnQueryTextListener searchListener; | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setHasOptionsMenu(true); | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { | ||||
|         View view = inflater.inflate(R.layout.repos_fragment, container, false); | ||||
|         View view = inflater.inflate(R.layout.fragment_repos, container, false); | ||||
|         unbinder = ButterKnife.bind(this, view); | ||||
|  | ||||
|         ButterKnife.bind(this, view); | ||||
|  | ||||
|         mSectionedAdapter = new | ||||
|                 SimpleSectionedRecyclerViewAdapter(getActivity(), R.layout.section, | ||||
|         mSectionedAdapter = new SimpleSectionedRecyclerViewAdapter(R.layout.section, | ||||
|                 R.id.section_text, new ReposAdapter(fUpdateRepos, fInstalledRepos, fOthersRepos)); | ||||
|  | ||||
|         recyclerView.setAdapter(mSectionedAdapter); | ||||
| @@ -65,10 +69,10 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList | ||||
|  | ||||
|         mSwipeRefreshLayout.setOnRefreshListener(() -> { | ||||
|             recyclerView.setVisibility(View.GONE); | ||||
|             new Async.LoadRepos(getActivity()).exec(); | ||||
|             new LoadRepos(getActivity()).exec(); | ||||
|         }); | ||||
|  | ||||
|         if (repoLoadDone.isTriggered) { | ||||
|         if (getApplication().repoLoadDone.isTriggered) { | ||||
|             reloadRepos(); | ||||
|             updateUI(); | ||||
|         } | ||||
| @@ -90,7 +94,7 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onTrigger(CallbackHandler.Event event) { | ||||
|     public void onTrigger(CallbackEvent<Void> event) { | ||||
|         Logger.dev("ReposFragment: UI refresh triggered"); | ||||
|         reloadRepos(); | ||||
|         updateUI(); | ||||
| @@ -104,21 +108,40 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
|         setHasOptionsMenu(true); | ||||
|         CallbackHandler.register(repoLoadDone, this); | ||||
|     public void onStart() { | ||||
|         super.onStart(); | ||||
|         getApplication().repoLoadDone.register(this); | ||||
|         getActivity().setTitle(R.string.downloads); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         CallbackHandler.unRegister(repoLoadDone, this); | ||||
|     public void onStop() { | ||||
|         getApplication().repoLoadDone.unRegister(this); | ||||
|         super.onStop(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         super.onDestroyView(); | ||||
|         unbinder.unbind(); | ||||
|     } | ||||
|  | ||||
|     private void reloadRepos() { | ||||
|         ModuleHelper.getRepoLists(mUpdateRepos, mInstalledRepos, mOthersRepos); | ||||
|         mUpdateRepos.clear(); | ||||
|         mInstalledRepos.clear(); | ||||
|         mOthersRepos.clear(); | ||||
|         for (Repo repo : getApplication().repoMap.values()) { | ||||
|             Module module = getApplication().moduleMap.get(repo.getId()); | ||||
|             if (module != null) { | ||||
|                 if (repo.getVersionCode() > module.getVersionCode()) { | ||||
|                     mUpdateRepos.add(repo); | ||||
|                 } else { | ||||
|                     mInstalledRepos.add(repo); | ||||
|                 } | ||||
|             } else { | ||||
|                 mOthersRepos.add(repo); | ||||
|             } | ||||
|         } | ||||
|         fUpdateRepos.clear(); | ||||
|         fInstalledRepos.clear(); | ||||
|         fOthersRepos.clear(); | ||||
| @@ -129,7 +152,7 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList | ||||
|  | ||||
|     private void updateUI() { | ||||
|         if (fUpdateRepos.size() + fInstalledRepos.size() + fOthersRepos.size() == 0) { | ||||
|             emptyTv.setVisibility(View.VISIBLE); | ||||
|             emptyRv.setVisibility(View.VISIBLE); | ||||
|             recyclerView.setVisibility(View.GONE); | ||||
|         } else { | ||||
|             List<SimpleSectionedRecyclerViewAdapter.Section> sections = new ArrayList<>(); | ||||
| @@ -144,13 +167,13 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList | ||||
|             } | ||||
|             SimpleSectionedRecyclerViewAdapter.Section[] array = sections.toArray(new SimpleSectionedRecyclerViewAdapter.Section[sections.size()]); | ||||
|             mSectionedAdapter.setSections(array); | ||||
|             emptyTv.setVisibility(View.GONE); | ||||
|             emptyRv.setVisibility(View.GONE); | ||||
|             recyclerView.setVisibility(View.VISIBLE); | ||||
|         } | ||||
|         mSwipeRefreshLayout.setRefreshing(false); | ||||
|     } | ||||
|  | ||||
|     private class FilterApps extends Async.NormalTask<String, Void, Void> { | ||||
|     private class FilterApps extends ParallelTask<String, Void, Void> { | ||||
|         @Override | ||||
|         protected Void doInBackground(String... strings) { | ||||
|             String newText = strings[0]; | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,50 +1,57 @@ | ||||
| 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.content.SharedPreferences; | ||||
| import android.os.Bundle; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.support.v7.app.AppCompatActivity; | ||||
| import android.text.TextUtils; | ||||
|  | ||||
| import com.topjohnwu.magisk.utils.Async; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.asyncs.GetBootBlocks; | ||||
| import com.topjohnwu.magisk.asyncs.LoadApps; | ||||
| import com.topjohnwu.magisk.asyncs.LoadModules; | ||||
| import com.topjohnwu.magisk.asyncs.LoadRepos; | ||||
| import com.topjohnwu.magisk.components.Activity; | ||||
| import com.topjohnwu.magisk.services.UpdateCheckService; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| public class SplashActivity extends AppCompatActivity { | ||||
| public class SplashActivity extends Activity{ | ||||
|  | ||||
|     private static final int UPDATE_SERVICE_ID = 1; | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|  | ||||
|         super.onCreate(savedInstanceState); | ||||
|         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplication()); | ||||
|         if (prefs.getString("theme", "").equals("Dark")) { | ||||
|             setTheme(R.style.AppTheme_dh); | ||||
|  | ||||
|         // Init the info and configs and root shell | ||||
|         getApplicationContext().init(); | ||||
|  | ||||
|         // Now fire all async tasks | ||||
|         new GetBootBlocks(this).exec(); | ||||
|         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); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Logger.devLog = prefs.getBoolean("developer_logging", false); | ||||
|         Logger.logShell = prefs.getBoolean("shell_logging", false); | ||||
|  | ||||
|         // Initialize prefs | ||||
|         prefs.edit() | ||||
|                 .putBoolean("magiskhide", Utils.itemExist(false, "/magisk/.core/magiskhide/enable")) | ||||
|                 .putBoolean("busybox", Utils.commandExists("busybox")) | ||||
|                 .putBoolean("hosts", Utils.itemExist(false, "/magisk/.core/hosts")) | ||||
|                 .apply(); | ||||
|  | ||||
|         // Start all async tasks | ||||
|         new Async.GetBootBlocks().exec(); | ||||
|         new Async.CheckUpdates().exec(); | ||||
|         Async.checkSafetyNet(getApplicationContext()); | ||||
|         new Async.LoadModules() { | ||||
|             @Override | ||||
|             protected void onPostExecute(Void v) { | ||||
|                 super.onPostExecute(v); | ||||
|                 new Async.LoadRepos(getApplicationContext()).exec(); | ||||
|             } | ||||
|         }.exec(); | ||||
|  | ||||
|         // Start main activity | ||||
|         Intent intent = new Intent(getApplicationContext(), MainActivity.class); | ||||
|         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,246 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.app.Fragment; | ||||
| import android.app.FragmentTransaction; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v4.widget.SwipeRefreshLayout; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.ProgressBar; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.utils.Async; | ||||
| import com.topjohnwu.magisk.utils.CallbackHandler; | ||||
| import com.topjohnwu.magisk.utils.Logger; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import butterknife.BindColor; | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
|  | ||||
| public class StatusFragment extends Fragment implements CallbackHandler.EventListener { | ||||
|  | ||||
|     public static double magiskVersion, remoteMagiskVersion = -1; | ||||
|     public static String magiskVersionString, magiskLink, magiskChangelog; | ||||
|     public static int SNCheckResult = -1; | ||||
|  | ||||
|     public static final CallbackHandler.Event updateCheckDone = new CallbackHandler.Event(); | ||||
|     public static final CallbackHandler.Event safetyNetDone = new CallbackHandler.Event(); | ||||
|  | ||||
|     @BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout; | ||||
|  | ||||
|     @BindView(R.id.magisk_status_container) View magiskStatusContainer; | ||||
|     @BindView(R.id.magisk_status_icon) ImageView magiskStatusIcon; | ||||
|     @BindView(R.id.magisk_version) TextView magiskVersionText; | ||||
|     @BindView(R.id.magisk_update_status) TextView magiskUpdateText; | ||||
|     @BindView(R.id.magisk_check_updates_progress) ProgressBar magiskCheckUpdatesProgress; | ||||
|  | ||||
|     @BindView(R.id.root_status_container) View rootStatusContainer; | ||||
|     @BindView(R.id.root_status_icon) ImageView rootStatusIcon; | ||||
|     @BindView(R.id.root_status) TextView rootStatusText; | ||||
|     @BindView(R.id.root_info) TextView rootInfoText; | ||||
|  | ||||
|     @BindView(R.id.safetyNet_container) View safetyNetContainer; | ||||
|     @BindView(R.id.safetyNet_icon) ImageView safetyNetIcon; | ||||
|     @BindView(R.id.safetyNet_status) TextView safetyNetStatusText; | ||||
|     @BindView(R.id.safetyNet_check_progress) ProgressBar safetyNetProgress; | ||||
|  | ||||
|     @BindColor(R.color.red500) int colorBad; | ||||
|     @BindColor(R.color.green500) int colorOK; | ||||
|     @BindColor(R.color.yellow500) int colorWarn; | ||||
|     @BindColor(R.color.grey500) int colorNeutral; | ||||
|     @BindColor(R.color.blue500) int colorInfo; | ||||
|     @BindColor(android.R.color.transparent) int trans; | ||||
|  | ||||
|     static { | ||||
|         checkMagiskInfo(); | ||||
|     } | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { | ||||
|         View v = inflater.inflate(R.layout.status_fragment, container, false); | ||||
|         ButterKnife.bind(this, v); | ||||
|  | ||||
|         mSwipeRefreshLayout.setOnRefreshListener(() -> { | ||||
|             magiskStatusContainer.setBackgroundColor(trans); | ||||
|             magiskStatusIcon.setImageResource(0); | ||||
|             magiskUpdateText.setText(R.string.checking_for_updates); | ||||
|             magiskCheckUpdatesProgress.setVisibility(View.VISIBLE); | ||||
|  | ||||
|             safetyNetProgress.setVisibility(View.VISIBLE); | ||||
|             safetyNetContainer.setBackgroundColor(trans); | ||||
|             safetyNetIcon.setImageResource(0); | ||||
|             safetyNetStatusText.setText(R.string.checking_safetyNet_status); | ||||
|  | ||||
|             updateUI(); | ||||
|             new Async.CheckUpdates().exec(); | ||||
|             Async.checkSafetyNet(getActivity()); | ||||
|         }); | ||||
|  | ||||
|         updateUI(); | ||||
|         if (updateCheckDone.isTriggered) { | ||||
|             updateCheckUI(); | ||||
|         } | ||||
|         if (safetyNetDone.isTriggered) { | ||||
|             updateSafetyNetUI(); | ||||
|         } | ||||
|  | ||||
|         if (magiskVersion < 0 && Shell.rootAccess()) { | ||||
|             MainActivity.alertBuilder | ||||
|                     .setTitle(R.string.no_magisk_title) | ||||
|                     .setMessage(R.string.no_magisk_msg) | ||||
|                     .setCancelable(true) | ||||
|                     .setPositiveButton(R.string.goto_install, (dialogInterface, i) -> { | ||||
|                         ((MainActivity) getActivity()).navigationView.setCheckedItem(R.id.install); | ||||
|                         FragmentTransaction transaction = getFragmentManager().beginTransaction(); | ||||
|                         transaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out); | ||||
|                         try { | ||||
|                             transaction.replace(R.id.content_frame, new InstallFragment(), "install").commit(); | ||||
|                         } catch (IllegalStateException ignored) {} | ||||
|                     }) | ||||
|                     .setNegativeButton(R.string.no_thanks, null) | ||||
|                     .show(); | ||||
|         } | ||||
|  | ||||
|         return v; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onTrigger(CallbackHandler.Event event) { | ||||
|         if (event == updateCheckDone) { | ||||
|             Logger.dev("StatusFragment: Update Check UI refresh triggered"); | ||||
|             updateCheckUI(); | ||||
|         } else if (event == safetyNetDone) { | ||||
|             Logger.dev("StatusFragment: SafetyNet UI refresh triggered"); | ||||
|             updateSafetyNetUI(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
|         CallbackHandler.register(updateCheckDone, this); | ||||
|         CallbackHandler.register(safetyNetDone, this); | ||||
|         getActivity().setTitle(R.string.status); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         super.onDestroy(); | ||||
|         CallbackHandler.unRegister(updateCheckDone, this); | ||||
|         CallbackHandler.unRegister(safetyNetDone, this); | ||||
|     } | ||||
|  | ||||
|     private static void checkMagiskInfo() { | ||||
|         List<String> ret = Shell.sh("getprop magisk.version"); | ||||
|         if (ret.get(0).length() == 0) { | ||||
|             magiskVersion = -1; | ||||
|         } else { | ||||
|             try { | ||||
|                 magiskVersionString = ret.get(0); | ||||
|                 magiskVersion = Double.parseDouble(ret.get(0)); | ||||
|             } catch (NumberFormatException e) { | ||||
|                 // Custom version don't need to receive updates | ||||
|                 magiskVersion = Double.POSITIVE_INFINITY; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void updateUI() { | ||||
|         int image, color; | ||||
|  | ||||
|         checkMagiskInfo(); | ||||
|  | ||||
|         if (magiskVersion < 0) { | ||||
|             magiskVersionText.setText(R.string.magisk_version_error); | ||||
|         } else { | ||||
|             magiskVersionText.setText(getString(R.string.magisk_version, magiskVersionString)); | ||||
|         } | ||||
|  | ||||
|         if (Shell.rootStatus == 1) { | ||||
|             color = colorOK; | ||||
|             image = R.drawable.ic_check_circle; | ||||
|             rootStatusText.setText(R.string.proper_root); | ||||
|             rootInfoText.setText(Shell.sh("su -v").get(0)); | ||||
|  | ||||
|         } else { | ||||
|             rootInfoText.setText(R.string.root_info_warning); | ||||
|             if (Shell.rootStatus == 0) { | ||||
|                 color = colorBad; | ||||
|                 image = R.drawable.ic_cancel; | ||||
|                 rootStatusText.setText(R.string.not_rooted); | ||||
|             } else { | ||||
|                 color = colorNeutral; | ||||
|                 image = R.drawable.ic_help; | ||||
|                 rootStatusText.setText(R.string.root_error); | ||||
|             } | ||||
|         } | ||||
|         rootStatusContainer.setBackgroundColor(color); | ||||
|         rootStatusText.setTextColor(color); | ||||
|         rootInfoText.setTextColor(color); | ||||
|         rootStatusIcon.setImageResource(image); | ||||
|     } | ||||
|  | ||||
|     private void updateCheckUI() { | ||||
|         int image, color; | ||||
|  | ||||
|         if (remoteMagiskVersion < 0) { | ||||
|             color = colorNeutral; | ||||
|             image = R.drawable.ic_help; | ||||
|             magiskUpdateText.setText(R.string.cannot_check_updates); | ||||
|         } else if (remoteMagiskVersion > magiskVersion) { | ||||
|             color = colorInfo; | ||||
|             image = R.drawable.ic_update; | ||||
|             magiskUpdateText.setText(getString(R.string.magisk_update_available, remoteMagiskVersion)); | ||||
|         } else { | ||||
|             color = colorOK; | ||||
|             image = R.drawable.ic_check_circle; | ||||
|             magiskUpdateText.setText(getString(R.string.up_to_date, getString(R.string.magisk))); | ||||
|         } | ||||
|  | ||||
|         if (magiskVersion < 0) { | ||||
|             color = colorBad; | ||||
|             image = R.drawable.ic_cancel; | ||||
|         } | ||||
|         magiskStatusContainer.setBackgroundColor(color); | ||||
|         magiskVersionText.setTextColor(color); | ||||
|         magiskUpdateText.setTextColor(color); | ||||
|         magiskStatusIcon.setImageResource(image); | ||||
|  | ||||
|         magiskCheckUpdatesProgress.setVisibility(View.GONE); | ||||
|         mSwipeRefreshLayout.setRefreshing(false); | ||||
|     } | ||||
|  | ||||
|     private void updateSafetyNetUI() { | ||||
|         int image, color; | ||||
|         safetyNetProgress.setVisibility(View.GONE); | ||||
|         switch (SNCheckResult) { | ||||
|             case -1: | ||||
|                 color = colorNeutral; | ||||
|                 image = R.drawable.ic_help; | ||||
|                 safetyNetStatusText.setText(R.string.safetyNet_error); | ||||
|                 break; | ||||
|             case 0: | ||||
|                 color = colorBad; | ||||
|                 image = R.drawable.ic_cancel; | ||||
|                 safetyNetStatusText.setText(R.string.safetyNet_fail); | ||||
|                 break; | ||||
|             case 1: | ||||
|             default: | ||||
|                 color = colorOK; | ||||
|                 image = R.drawable.ic_check_circle; | ||||
|                 safetyNetStatusText.setText(R.string.safetyNet_pass); | ||||
|                 break; | ||||
|         } | ||||
|         safetyNetContainer.setBackgroundColor(color); | ||||
|         safetyNetStatusText.setTextColor(color); | ||||
|         safetyNetIcon.setImageResource(image); | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										90
									
								
								app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuInflater; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.adapters.SuLogAdapter; | ||||
| import com.topjohnwu.magisk.components.Fragment; | ||||
| import com.topjohnwu.magisk.superuser.SuLogEntry; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import butterknife.Unbinder; | ||||
|  | ||||
| public class SuLogFragment extends Fragment { | ||||
|  | ||||
|     @BindView(R.id.empty_rv) TextView emptyRv; | ||||
|     @BindView(R.id.recyclerView) RecyclerView recyclerView; | ||||
|  | ||||
|     private Unbinder unbinder; | ||||
|     private MagiskManager magiskManager; | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setHasOptionsMenu(true); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { | ||||
|         inflater.inflate(R.menu.menu_log, menu); | ||||
|         menu.findItem(R.id.menu_save).setVisible(false); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, ViewGroup container, | ||||
|                              Bundle savedInstanceState) { | ||||
|         // Inflate the layout for this fragment | ||||
|         View v = inflater.inflate(R.layout.fragment_su_log, container, false); | ||||
|         unbinder = ButterKnife.bind(this, v); | ||||
|         magiskManager = getApplication(); | ||||
|  | ||||
|         updateList(); | ||||
|  | ||||
|         return v; | ||||
|     } | ||||
|  | ||||
|     private void updateList() { | ||||
|         List<SuLogEntry> logs = magiskManager.suDB.getLogList(); | ||||
|  | ||||
|         if (logs.size() == 0) { | ||||
|             emptyRv.setVisibility(View.VISIBLE); | ||||
|             recyclerView.setVisibility(View.GONE); | ||||
|         } else { | ||||
|             recyclerView.setAdapter(new SuLogAdapter(logs).getAdapter()); | ||||
|             emptyRv.setVisibility(View.GONE); | ||||
|             recyclerView.setVisibility(View.VISIBLE); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         switch (item.getItemId()) { | ||||
|             case R.id.menu_refresh: | ||||
|                 updateList(); | ||||
|                 return true; | ||||
|             case R.id.menu_clear: | ||||
|                 magiskManager.suDB.clearLogs(); | ||||
|                 updateList(); | ||||
|                 return true; | ||||
|             default: | ||||
|                 return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         super.onDestroyView(); | ||||
|         unbinder.unbind(); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,63 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.content.pm.PackageManager; | ||||
| import android.os.Bundle; | ||||
| import android.support.annotation.Nullable; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.adapters.PolicyAdapter; | ||||
| import com.topjohnwu.magisk.components.Fragment; | ||||
| import com.topjohnwu.magisk.superuser.Policy; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
| import butterknife.Unbinder; | ||||
|  | ||||
| public class SuperuserFragment extends Fragment { | ||||
|  | ||||
|     private Unbinder unbinder; | ||||
|     @BindView(R.id.recyclerView) RecyclerView recyclerView; | ||||
|     @BindView(R.id.empty_rv) TextView emptyRv; | ||||
|  | ||||
|     @Nullable | ||||
|     @Override | ||||
|     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { | ||||
|         View view = inflater.inflate(R.layout.fragment_superuser, container, false); | ||||
|         unbinder = ButterKnife.bind(this, view); | ||||
|  | ||||
|         PackageManager pm = getActivity().getPackageManager(); | ||||
|         MagiskManager magiskManager = getApplication(); | ||||
|  | ||||
|         List<Policy> policyList = magiskManager.suDB.getPolicyList(pm); | ||||
|  | ||||
|         if (policyList.size() == 0) { | ||||
|             emptyRv.setVisibility(View.VISIBLE); | ||||
|             recyclerView.setVisibility(View.GONE); | ||||
|         } else { | ||||
|             recyclerView.setAdapter(new PolicyAdapter(policyList, magiskManager.suDB, pm)); | ||||
|             emptyRv.setVisibility(View.GONE); | ||||
|             recyclerView.setVisibility(View.VISIBLE); | ||||
|         } | ||||
|  | ||||
|         return view; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onStart() { | ||||
|         super.onStart(); | ||||
|         getActivity().setTitle(getString(R.string.superuser)); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         super.onDestroyView(); | ||||
|         unbinder.unbind(); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,22 +1,25 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.content.pm.ApplicationInfo; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.support.design.widget.Snackbar; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.util.DisplayMetrics; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.view.WindowManager; | ||||
| import android.widget.CheckBox; | ||||
| import android.widget.Filter; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.utils.Async; | ||||
| import com.topjohnwu.magisk.asyncs.MagiskHide; | ||||
| import com.topjohnwu.magisk.components.SnackbarMaker; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Arrays; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| @@ -24,32 +27,38 @@ import butterknife.ButterKnife; | ||||
|  | ||||
| public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> { | ||||
|  | ||||
|     private List<ApplicationInfo> mList; | ||||
|     private List<String> mHideList; | ||||
|     private Context context; | ||||
|     private PackageManager packageManager; | ||||
|  | ||||
|     // Don't show in list... | ||||
|     private static final String[] blackList = { | ||||
|     public static final List<String> BLACKLIST =  Arrays.asList( | ||||
|             "android", | ||||
|             "com.topjohnwu.magisk", | ||||
|             "com.google.android.gms" | ||||
|     }; | ||||
|     ); | ||||
|  | ||||
|     public ApplicationAdapter(List<ApplicationInfo> list, List<String> hideList) { | ||||
|         mList = list; | ||||
|     private static final List<String> SNLIST =  Arrays.asList( | ||||
|             "com.google.android.apps.walletnfcrel", | ||||
|             "com.nianticlabs.pokemongo" | ||||
|     ); | ||||
|  | ||||
|     private List<ApplicationInfo> mOriginalList, mList; | ||||
|     private List<String> mHideList; | ||||
|     private PackageManager packageManager; | ||||
|     private ApplicationFilter filter; | ||||
|  | ||||
|     public ApplicationAdapter(PackageManager packageManager) { | ||||
|         mOriginalList = mList = Collections.emptyList(); | ||||
|         mHideList = Collections.emptyList(); | ||||
|         this.packageManager = packageManager; | ||||
|         filter = new ApplicationFilter(); | ||||
|     } | ||||
|  | ||||
|     public void setLists(List<ApplicationInfo> listApps, List<String> hideList) { | ||||
|         mOriginalList = mList = listApps; | ||||
|         mHideList = hideList; | ||||
|         List<String> bl = Arrays.asList(blackList); | ||||
|         for (int i = 0; i < mList.size(); ++i) | ||||
|             if (bl.contains(mList.get(i).packageName)) | ||||
|                 mList.remove(i); | ||||
|         notifyDataSetChanged(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | ||||
|         View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false); | ||||
|         context = parent.getContext(); | ||||
|         packageManager = context.getPackageManager(); | ||||
|         ButterKnife.bind(this, mView); | ||||
|         return new ViewHolder(mView); | ||||
|     } | ||||
|  | ||||
| @@ -60,26 +69,31 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter. | ||||
|         holder.appIcon.setImageDrawable(info.loadIcon(packageManager)); | ||||
|         holder.appName.setText(info.loadLabel(packageManager)); | ||||
|         holder.appPackage.setText(info.packageName); | ||||
|         holder.checkBox.setChecked(false); | ||||
|  | ||||
|         for (String hidePackage : mHideList) { | ||||
|             if (info.packageName.contains(hidePackage)) { | ||||
|                 holder.checkBox.setChecked(true); | ||||
|                 break; | ||||
|             } | ||||
|         // Remove all listeners | ||||
|         holder.itemView.setOnClickListener(null); | ||||
|         holder.checkBox.setOnCheckedChangeListener(null); | ||||
|  | ||||
|         if (SNLIST.contains(info.packageName)) { | ||||
|             holder.checkBox.setChecked(true); | ||||
|             holder.checkBox.setEnabled(false); | ||||
|             holder.itemView.setOnClickListener(v -> | ||||
|                 SnackbarMaker.make(holder.itemView, | ||||
|                         R.string.safetyNet_hide_notice, Snackbar.LENGTH_LONG).show() | ||||
|             ); | ||||
|         } else { | ||||
|             holder.checkBox.setEnabled(true); | ||||
|             holder.checkBox.setChecked(mHideList.contains(info.packageName)); | ||||
|             holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> { | ||||
|                 if (isChecked) { | ||||
|                     new MagiskHide().add(info.packageName); | ||||
|                     mHideList.add(info.packageName); | ||||
|                 } else { | ||||
|                     new MagiskHide().rm(info.packageName); | ||||
|                     mHideList.remove(info.packageName); | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         holder.checkBox.setOnClickListener(v -> { | ||||
|             CheckBox chkbox = (CheckBox) v; | ||||
|             if (chkbox.isChecked()) { | ||||
|                 new Async.MagiskHide().add(info.packageName); | ||||
|                 mHideList.add(info.packageName); | ||||
|             } | ||||
|             else { | ||||
|                 new Async.MagiskHide().rm(info.packageName); | ||||
|                 mHideList.remove(info.packageName); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -87,19 +101,52 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter. | ||||
|         return mList.size(); | ||||
|     } | ||||
|  | ||||
|     class ViewHolder extends RecyclerView.ViewHolder { | ||||
|     public void filter(String constraint) { | ||||
|         filter.filter(constraint); | ||||
|     } | ||||
|  | ||||
|     static class ViewHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         @BindView(R.id.app_icon) ImageView appIcon; | ||||
|         @BindView(R.id.app_name) TextView appName; | ||||
|         @BindView(R.id.app_package) TextView appPackage; | ||||
|         @BindView(R.id.checkbox) CheckBox checkBox; | ||||
|  | ||||
|         public ViewHolder(View itemView) { | ||||
|         ViewHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             WindowManager windowmanager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); | ||||
|             ButterKnife.bind(this, itemView); | ||||
|             DisplayMetrics dimension = new DisplayMetrics(); | ||||
|             windowmanager.getDefaultDisplay().getMetrics(dimension); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|     private class ApplicationFilter extends Filter { | ||||
|  | ||||
|         @Override | ||||
|         protected FilterResults performFiltering(CharSequence constraint) { | ||||
|             List<ApplicationInfo> filteredApps; | ||||
|             if (constraint == null || constraint.length() == 0) { | ||||
|                 filteredApps = mOriginalList; | ||||
|             } else { | ||||
|                 filteredApps = new ArrayList<>(); | ||||
|                 String filter = constraint.toString().toLowerCase(); | ||||
|                 for (ApplicationInfo info : mOriginalList) { | ||||
|                     if (Utils.lowercaseContains(info.loadLabel(packageManager), filter) | ||||
|                             || Utils.lowercaseContains(info.packageName, filter)) { | ||||
|                         filteredApps.add(info); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|             FilterResults results = new FilterResults(); | ||||
|             results.values = filteredApps; | ||||
|             results.count = filteredApps.size(); | ||||
|             return results; | ||||
|         } | ||||
|  | ||||
|         @SuppressWarnings("unchecked") | ||||
|         @Override | ||||
|         protected void publishResults(CharSequence constraint, FilterResults results) { | ||||
|             mList = (List<ApplicationInfo>) results.values; | ||||
|             notifyDataSetChanged(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3,18 +3,17 @@ package com.topjohnwu.magisk.adapters; | ||||
| import android.content.Context; | ||||
| import android.support.design.widget.Snackbar; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.util.DisplayMetrics; | ||||
| import android.text.TextUtils; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.view.WindowManager; | ||||
| import android.widget.CheckBox; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.components.SnackbarMaker; | ||||
| import com.topjohnwu.magisk.module.Module; | ||||
| import com.topjohnwu.magisk.utils.Async; | ||||
| import com.topjohnwu.magisk.utils.Shell; | ||||
|  | ||||
| import java.util.List; | ||||
| @@ -25,7 +24,6 @@ import butterknife.ButterKnife; | ||||
| public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHolder> { | ||||
|  | ||||
|     private final List<Module> mList; | ||||
|     private Context context; | ||||
|  | ||||
|     public ModulesAdapter(List<Module> list) { | ||||
|         mList = list; | ||||
| @@ -34,90 +32,50 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold | ||||
|     @Override | ||||
|     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | ||||
|         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false); | ||||
|         context = parent.getContext(); | ||||
|         ButterKnife.bind(this, view); | ||||
|         return new ViewHolder(view); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBindViewHolder(final ViewHolder holder, int position) { | ||||
|         Context context = holder.itemView.getContext(); | ||||
|         final Module module = mList.get(position); | ||||
|         holder.title.setText(module.getName()); | ||||
|  | ||||
|         String version = module.getVersion(); | ||||
|         String author = module.getAuthor(); | ||||
|         String versionName = module.getVersion(); | ||||
|         String description = module.getDescription(); | ||||
|         if (versionName != null) { | ||||
|             holder.versionName.setText(versionName); | ||||
|         } | ||||
|         if (author != null) { | ||||
|             holder.author.setText(context.getString(R.string.author, author)); | ||||
|         } | ||||
|         if (description != null) { | ||||
|             holder.description.setText(description); | ||||
|         } | ||||
|         String noInfo = context.getString(R.string.no_info_provided); | ||||
|  | ||||
|         holder.title.setText(module.getName()); | ||||
|         holder.versionName.setText( TextUtils.isEmpty(version) ? noInfo : version); | ||||
|         holder.author.setText( TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author)); | ||||
|         holder.description.setText( TextUtils.isEmpty(description) ? noInfo : description); | ||||
|  | ||||
|         holder.checkBox.setOnCheckedChangeListener(null); | ||||
|         holder.checkBox.setChecked(module.isEnabled()); | ||||
|         holder.checkBox.setOnClickListener((v) -> { | ||||
|             CheckBox checkBox = (CheckBox) v; | ||||
|             if (checkBox.isChecked()) { | ||||
|                 new Async.RootTask<Void, Void, Void>() { | ||||
|                     @Override | ||||
|                     protected Void doInBackground(Void... voids) { | ||||
|                         module.removeDisableFile(); | ||||
|                         return null; | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     protected void onPostExecute(Void v) { | ||||
|                         Snackbar.make(holder.title, R.string.disable_file_removed, Snackbar.LENGTH_SHORT).show(); | ||||
|                     } | ||||
|                 }.exec(); | ||||
|         holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> { | ||||
|             int snack; | ||||
|             if (isChecked) { | ||||
|                 module.removeDisableFile(); | ||||
|                 snack = R.string.disable_file_removed; | ||||
|             } else { | ||||
|                 new Async.RootTask<Void, Void, Void>() { | ||||
|                     @Override | ||||
|                     protected Void doInBackground(Void... voids) { | ||||
|                         module.createDisableFile(); | ||||
|                         return null; | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     protected void onPostExecute(Void v) { | ||||
|                         Snackbar.make(holder.title, R.string.disable_file_created, Snackbar.LENGTH_SHORT).show(); | ||||
|                     } | ||||
|                 }.exec(); | ||||
|                 module.createDisableFile(); | ||||
|                 snack = R.string.disable_file_created; | ||||
|             } | ||||
|             SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); | ||||
|         }); | ||||
|  | ||||
|         holder.delete.setOnClickListener(v -> { | ||||
|             if (module.willBeRemoved()) { | ||||
|                 new Async.RootTask<Void, Void, Void>() { | ||||
|                     @Override | ||||
|                     protected Void doInBackground(Void... voids) { | ||||
|                         module.deleteRemoveFile(); | ||||
|                         return null; | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     protected void onPostExecute(Void v) { | ||||
|                         Snackbar.make(holder.title, R.string.remove_file_deleted, Snackbar.LENGTH_SHORT).show(); | ||||
|                         updateDeleteButton(holder, module); | ||||
|                     } | ||||
|                 }.exec(); | ||||
|             boolean removed = module.willBeRemoved(); | ||||
|             int snack; | ||||
|             if (removed) { | ||||
|                 module.deleteRemoveFile(); | ||||
|                 snack = R.string.remove_file_deleted; | ||||
|             } else { | ||||
|                 new Async.RootTask<Void, Void, Void>() { | ||||
|                     @Override | ||||
|                     protected Void doInBackground(Void... voids) { | ||||
|                         module.createRemoveFile(); | ||||
|                         return null; | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     protected void onPostExecute(Void v) { | ||||
|                         Snackbar.make(holder.title, R.string.remove_file_created, Snackbar.LENGTH_SHORT).show(); | ||||
|                         updateDeleteButton(holder, module); | ||||
|                     } | ||||
|                 }.exec(); | ||||
|                 module.createRemoveFile(); | ||||
|                 snack = R.string.remove_file_created; | ||||
|             } | ||||
|             SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); | ||||
|             updateDeleteButton(holder, module); | ||||
|         }); | ||||
|  | ||||
|         if (module.isUpdated()) { | ||||
| @@ -144,7 +102,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold | ||||
|         return mList.size(); | ||||
|     } | ||||
|  | ||||
|     class ViewHolder extends RecyclerView.ViewHolder { | ||||
|     static class ViewHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         @BindView(R.id.title) TextView title; | ||||
|         @BindView(R.id.version_name) TextView versionName; | ||||
| @@ -154,12 +112,9 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold | ||||
|         @BindView(R.id.author) TextView author; | ||||
|         @BindView(R.id.delete) ImageView delete; | ||||
|  | ||||
|         public ViewHolder(View itemView) { | ||||
|         ViewHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             WindowManager windowmanager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); | ||||
|             ButterKnife.bind(this, itemView); | ||||
|             DisplayMetrics dimension = new DisplayMetrics(); | ||||
|             windowmanager.getDefaultDisplay().getMetrics(dimension); | ||||
|  | ||||
|             if (!Shell.rootAccess()) { | ||||
|                 checkBox.setEnabled(false); | ||||
|   | ||||
| @@ -0,0 +1,144 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| import android.content.pm.PackageManager; | ||||
| import android.support.design.widget.Snackbar; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.Switch; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.components.AlertDialogBuilder; | ||||
| import com.topjohnwu.magisk.components.ExpandableViewHolder; | ||||
| import com.topjohnwu.magisk.components.SnackbarMaker; | ||||
| import com.topjohnwu.magisk.database.SuDatabaseHelper; | ||||
| import com.topjohnwu.magisk.superuser.Policy; | ||||
|  | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import butterknife.ButterKnife; | ||||
|  | ||||
| public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> { | ||||
|  | ||||
|     private List<Policy> policyList; | ||||
|     private SuDatabaseHelper dbHelper; | ||||
|     private PackageManager pm; | ||||
|     private Set<Policy> expandList = new HashSet<>(); | ||||
|  | ||||
|     public PolicyAdapter(List<Policy> list, SuDatabaseHelper db, PackageManager pm) { | ||||
|         policyList = list; | ||||
|         dbHelper = db; | ||||
|         this.pm = pm; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | ||||
|         View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_policy, parent, false); | ||||
|         return new ViewHolder(v); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBindViewHolder(ViewHolder holder, int position) { | ||||
|         Policy policy = policyList.get(position); | ||||
|  | ||||
|         holder.setExpanded(expandList.contains(policy)); | ||||
|  | ||||
|         holder.itemView.setOnClickListener(view -> { | ||||
|             if (holder.mExpanded) { | ||||
|                 holder.collapse(); | ||||
|                 expandList.remove(policy); | ||||
|             } else { | ||||
|                 holder.expand(); | ||||
|                 expandList.add(policy); | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         holder.appName.setText(policy.appName); | ||||
|         holder.packageName.setText(policy.packageName); | ||||
|         holder.appIcon.setImageDrawable(policy.info.loadIcon(pm)); | ||||
|         holder.masterSwitch.setOnCheckedChangeListener((v, isChecked) -> { | ||||
|             if ((isChecked && policy.policy == Policy.DENY) || | ||||
|                     (!isChecked && policy.policy == Policy.ALLOW)) { | ||||
|                 policy.policy = isChecked ? Policy.ALLOW : Policy.DENY; | ||||
|                 String message = v.getContext().getString( | ||||
|                         isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName); | ||||
|                 SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show(); | ||||
|                 dbHelper.updatePolicy(policy); | ||||
|             } | ||||
|         }); | ||||
|         holder.notificationSwitch.setOnCheckedChangeListener((v, isChecked) -> { | ||||
|             if ((isChecked && !policy.notification) || | ||||
|                     (!isChecked && policy.notification)) { | ||||
|                 policy.notification = isChecked; | ||||
|                 String message = v.getContext().getString( | ||||
|                         isChecked ? R.string.su_snack_notif_on : R.string.su_snack_notif_off, policy.appName); | ||||
|                 SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show(); | ||||
|                 dbHelper.updatePolicy(policy); | ||||
|             } | ||||
|         }); | ||||
|         holder.loggingSwitch.setOnCheckedChangeListener((v, isChecked) -> { | ||||
|             if ((isChecked && !policy.logging) || | ||||
|                     (!isChecked && policy.logging)) { | ||||
|                 policy.logging = isChecked; | ||||
|                 String message = v.getContext().getString( | ||||
|                         isChecked ? R.string.su_snack_log_on : R.string.su_snack_log_off, policy.appName); | ||||
|                 SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show(); | ||||
|                 dbHelper.updatePolicy(policy); | ||||
|             } | ||||
|         }); | ||||
|         holder.delete.setOnClickListener(v -> new AlertDialogBuilder(v.getContext()) | ||||
|                 .setTitle(R.string.su_revoke_title) | ||||
|                 .setMessage(v.getContext().getString(R.string.su_revoke_msg, policy.appName)) | ||||
|                 .setPositiveButton(R.string.yes, (dialog, which) -> { | ||||
|                     policyList.remove(position); | ||||
|                     notifyItemRemoved(position); | ||||
|                     notifyItemRangeChanged(position, policyList.size()); | ||||
|                     SnackbarMaker.make(holder.itemView, v.getContext().getString(R.string.su_snack_revoke, policy.appName), | ||||
|                             Snackbar.LENGTH_SHORT).show(); | ||||
|                     dbHelper.deletePolicy(policy); | ||||
|                 }) | ||||
|                 .setNegativeButton(R.string.no_thanks, null) | ||||
|                 .setCancelable(true) | ||||
|                 .show()); | ||||
|         holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW); | ||||
|         holder.notificationSwitch.setChecked(policy.notification); | ||||
|         holder.loggingSwitch.setChecked(policy.logging); | ||||
|  | ||||
|         // Hide for now | ||||
|         holder.moreInfo.setVisibility(View.GONE); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getItemCount() { | ||||
|         return policyList.size(); | ||||
|     } | ||||
|  | ||||
|     static class ViewHolder extends ExpandableViewHolder { | ||||
|  | ||||
|         @BindView(R.id.app_name) TextView appName; | ||||
|         @BindView(R.id.package_name) TextView packageName; | ||||
|         @BindView(R.id.app_icon) ImageView appIcon; | ||||
|         @BindView(R.id.master_switch) Switch masterSwitch; | ||||
|         @BindView(R.id.notification_switch) Switch notificationSwitch; | ||||
|         @BindView(R.id.logging_switch) Switch loggingSwitch; | ||||
|  | ||||
|         @BindView(R.id.delete) ImageView delete; | ||||
|         @BindView(R.id.more_info) ImageView moreInfo; | ||||
|  | ||||
|         public ViewHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             ButterKnife.bind(this, itemView); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void setExpandLayout(View itemView) { | ||||
|             expandLayout = itemView.findViewById(R.id.expand_layout); | ||||
|         } | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,6 +1,5 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.support.v7.widget.RecyclerView; | ||||
| import android.util.SparseArray; | ||||
| import android.view.LayoutInflater; | ||||
| @@ -13,25 +12,21 @@ import java.util.Comparator; | ||||
|  | ||||
| public class SimpleSectionedRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { | ||||
|  | ||||
|     private final Context mContext; | ||||
|     private static final int SECTION_TYPE = 0; | ||||
|  | ||||
|     private boolean mValid = true; | ||||
|     private int mSectionResourceId; | ||||
|     private int mTextResourceId; | ||||
|     private LayoutInflater mLayoutInflater; | ||||
|     private RecyclerView.Adapter mBaseAdapter; | ||||
|     private SparseArray<Section> mSections = new SparseArray<Section>(); | ||||
|  | ||||
|  | ||||
|     public SimpleSectionedRecyclerViewAdapter(Context context, int sectionResourceId, int textResourceId, | ||||
|     public SimpleSectionedRecyclerViewAdapter(int sectionResourceId, int textResourceId, | ||||
|                                               RecyclerView.Adapter baseAdapter) { | ||||
|  | ||||
|         mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); | ||||
|         mSectionResourceId = sectionResourceId; | ||||
|         mTextResourceId = textResourceId; | ||||
|         mBaseAdapter = baseAdapter; | ||||
|         mContext = context; | ||||
|  | ||||
|         mBaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() { | ||||
|             @Override | ||||
| @@ -74,7 +69,7 @@ public class SimpleSectionedRecyclerViewAdapter extends RecyclerView.Adapter<Rec | ||||
|     @Override | ||||
|     public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int typeView) { | ||||
|         if (typeView == SECTION_TYPE) { | ||||
|             final View view = LayoutInflater.from(mContext).inflate(mSectionResourceId, parent, false); | ||||
|             View view = LayoutInflater.from(parent.getContext()).inflate(mSectionResourceId, parent, false); | ||||
|             return new SectionViewHolder(view,mTextResourceId); | ||||
|         }else{ | ||||
|             return mBaseAdapter.onCreateViewHolder(parent, typeView -1); | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user