mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-10-31 10:40:52 +01:00 
			
		
		
		
	Compare commits
	
		
			224 Commits
		
	
	
		
			manager-v7
			...
			manager-v7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 6fe03d2795 | ||
|   | c595a87ccf | ||
|   | fac07c3913 | ||
|   | c63fdbbc6b | ||
|   | 2ff5d9606b | ||
|   | ed43452c1a | ||
|   | 8f28d4028f | ||
|   | b54543b18c | ||
|   | 966d6593ca | ||
|   | ad95b1c9d1 | ||
|   | 3bfa38c60a | ||
|   | 0bdbcad8be | ||
|   | 80cd85b061 | ||
|   | 89275270f3 | ||
|   | e7339ba619 | ||
|   | d9ad7d522c | ||
|   | ef0e22cc41 | ||
|   | 62db65bf18 | ||
|   | d5371f752c | ||
|   | a5f5e94115 | ||
|   | 67c3f40adb | ||
|   | ff7a0ba599 | ||
|   | b152c63102 | ||
|   | 415ff23be5 | ||
|   | b0d6de783e | ||
|   | ac28e6e5ca | ||
|   | a9350f50c9 | ||
|   | ed7babcbf1 | ||
|   | 61ebc335c4 | ||
|   | 0167bd76f1 | ||
|   | 79d704008b | ||
|   | 0a703585b0 | ||
|   | b27801a27c | ||
|   | a0cfce7cbc | ||
|   | 8b7144c986 | ||
|   | d3f5f5ee59 | ||
|   | a2a3c7f438 | ||
|   | 4496f82d5b | ||
|   | 09d531557d | ||
|   | 7fee82f731 | ||
|   | 475054c48a | ||
|   | a743d05751 | ||
|   | d1ed502e03 | ||
|   | 37744c7ab6 | ||
|   | b25c49725f | ||
|   | b245782c7e | ||
|   | a9f32baae0 | ||
|   | e7ef71865d | ||
|   | 88c4f72b37 | ||
|   | abbcdf91a5 | ||
|   | b876df6e21 | ||
|   | 4bb81f35d7 | ||
|   | ff20267b3f | ||
|   | 2c9586d811 | ||
|   | 2813d2031a | ||
|   | 4040a0242f | ||
|   | 781ec810d9 | ||
|   | 9e90a71c04 | ||
|   | 5571714b26 | ||
|   | e0d1f02ef5 | ||
|   | 1b729e5ff2 | ||
|   | 51e587d4e8 | ||
|   | ac9c55dbc1 | ||
|   | 0893ac3141 | ||
|   | fb40e96917 | ||
|   | 4ca25f74c6 | ||
|   | 7fda917b86 | ||
|   | e6bd5f2c40 | ||
|   | 8a904ee384 | ||
|   | 00a9f18a1e | ||
|   | 8d68ebb074 | ||
|   | 5f53cfb4a9 | ||
|   | a2fa8d8be1 | ||
|   | 70a3c78ebb | ||
|   | db218407b0 | ||
|   | d52210dd90 | ||
|   | f3cd9a096a | ||
|   | e426090a18 | ||
|   | cbe64fd559 | ||
|   | 63ea7a70bd | ||
|   | fb0998f7a2 | ||
|   | a9b00dd537 | ||
|   | 52eb059515 | ||
|   | 7640246255 | ||
|   | 52c83b2916 | ||
|   | d9cded0fc9 | ||
|   | 750c42caf1 | ||
|   | bbf650c6cf | ||
|   | a25dace7e0 | ||
|   | 14ff22fbcd | ||
|   | 07eb7dda2d | ||
|   | 54d1207f92 | ||
|   | 003e44fb84 | ||
|   | 515f346dcc | ||
|   | d4058175b4 | ||
|   | 2de984ae24 | ||
|   | 761a8bf2a9 | ||
|   | 6df7006b36 | ||
|   | aceb3ee863 | ||
|   | 11d716a3c8 | ||
|   | 7cc8c014eb | ||
|   | f21241d944 | ||
|   | a181fa0652 | ||
|   | 3f748b4d2a | ||
|   | 683450f9c6 | ||
|   | 6050c4e8ba | ||
|   | 158af8819a | ||
|   | 7787bb31fa | ||
|   | a1fe3e7ccd | ||
|   | 4316028b23 | ||
|   | f2b52755d6 | ||
|   | adbd47a36c | ||
|   | ce693aa5e9 | ||
|   | ad80804461 | ||
|   | 2d55632430 | ||
|   | e81f00ef1a | ||
|   | 93fb0e3d74 | ||
|   | 71ce0de606 | ||
|   | 0407062c1d | ||
|   | f315c4416b | ||
|   | cda14af208 | ||
|   | 258f170cd7 | ||
|   | f76015d714 | ||
|   | 7e5e14163c | ||
|   | bcd1064e94 | ||
|   | 8a8441c875 | ||
|   | 15aa813416 | ||
|   | 605faccffd | ||
|   | 79f2d08c81 | ||
|   | 0568ae5391 | ||
|   | 5330dda9f8 | ||
|   | ebab126579 | ||
|   | 0e5417a13e | ||
|   | 9a968e0584 | ||
|   | ffec64d209 | ||
|   | f332746188 | ||
|   | b2fa5b551e | ||
|   | 36e83edddc | ||
|   | 6b045eadef | ||
|   | 147264822c | ||
|   | 36e4ccd800 | ||
|   | 796c16237d | ||
|   | 861ad9881c | ||
|   | 3101c538e9 | ||
|   | 42adc7382f | ||
|   | 9bb4dfad13 | ||
|   | 4e7dafb0e4 | ||
|   | bd00ae8ede | ||
|   | f309522268 | ||
|   | a6395d35db | ||
|   | a028cd5cec | ||
|   | 540000d26e | ||
|   | 888c656aa8 | ||
|   | 0efaddff23 | ||
|   | 94ba7cb0c5 | ||
|   | 2d58c725e0 | ||
|   | e035523eb8 | ||
|   | bea5308ab7 | ||
|   | f006a85fec | ||
|   | ea93013ebc | ||
|   | 8d4c407201 | ||
|   | fdeede23f7 | ||
|   | 53c5ca59b6 | ||
|   | 679db97209 | ||
|   | fbdd72273e | ||
|   | 0165602515 | ||
|   | 96127f8bd1 | ||
|   | 0dbdf336d6 | ||
|   | 48879df2da | ||
|   | b067a5bb13 | ||
|   | 4b54cf1288 | ||
|   | 6128c24f96 | ||
|   | d9c58f307f | ||
|   | b521fbeeda | ||
|   | d00a3b89f2 | ||
|   | 3d15518191 | ||
|   | 9b6535fdf5 | ||
|   | e0424fdba3 | ||
|   | 7481c53451 | ||
|   | 7219947237 | ||
|   | b72004e9cc | ||
|   | f187213568 | ||
|   | fc0df84edd | ||
|   | f24df4f43d | ||
|   | dab32e1599 | ||
|   | bc286fd4d3 | ||
|   | befe1a83b5 | ||
|   | 82ea9db9fd | ||
|   | c5758b3f2d | ||
|   | ace3708c9c | ||
|   | fc5026d268 | ||
|   | 77fd0e54be | ||
|   | 24490e0ff5 | ||
|   | da3937ff4e | ||
|   | ebe1ab982e | ||
|   | 98590cb00d | ||
|   | ff95f634f0 | ||
|   | ced9b4a8ee | ||
|   | 7af7910e78 | ||
|   | a4f5d47e72 | ||
|   | 6953cc2411 | ||
|   | 6a0b2ddee9 | ||
|   | 24f5bc98d8 | ||
|   | 5203886f0b | ||
|   | c10b376575 | ||
|   | ceb21ced2b | ||
|   | 86789a8694 | ||
|   | ca2235aee7 | ||
|   | a385e5cd92 | ||
|   | 0c7a95bdf6 | ||
|   | 036b5acf42 | ||
|   | 056dafc59f | ||
|   | a9c90718d6 | ||
|   | cc77a24502 | ||
|   | 71a91ac7a7 | ||
|   | 08a70f033a | ||
|   | 1b0c36dbd5 | ||
|   | 91da1cf817 | ||
|   | c577a9525d | ||
|   | 0149b1368d | ||
|   | cd6bcb97ef | ||
|   | df4161ffcc | ||
|   | 7a133eaf03 | ||
|   | 1cd45b53b1 | 
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							| @@ -16,3 +16,4 @@ chromeos/** binary | ||||
| *.exe binary | ||||
| *.apk binary | ||||
| *.png binary | ||||
| *.jpg binary | ||||
|   | ||||
							
								
								
									
										21
									
								
								README.MD
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								README.MD
									
									
									
									
									
								
							| @@ -38,6 +38,27 @@ Default string resources for Magisk Manager are scattered throughout | ||||
|  | ||||
| Translate each and place them in the respective locations (`<module>/src/main/res/values-<lang>/strings.xml`). | ||||
|  | ||||
| ## Signature Verification | ||||
|  | ||||
| Official release zips and APKs are signed with my personal private key. You can verify the key certificate to make sure the binaries you downloaded are not manipulated in anyway. | ||||
|  | ||||
| ``` bash | ||||
| # Use the keytool command from JDK to print certificates | ||||
| keytool -printcert -jarfile <APK or Magisk zip> | ||||
|  | ||||
| # The output should contain the following signature | ||||
| Owner: CN=John Wu, L=Taipei, C=TW | ||||
| Issuer: CN=John Wu, L=Taipei, C=TW | ||||
| Serial number: 50514879 | ||||
| Valid from: Sun Aug 14 13:23:44 EDT 2016 until: Tue Jul 21 13:23:44 EDT 2116 | ||||
| Certificate fingerprints: | ||||
| 	 MD5:  CE:DA:68:C1:E1:74:71:0A:EF:58:89:7D:AE:6E:AB:4F | ||||
| 	 SHA1: DC:0F:2B:61:CB:D7:E9:D3:DB:BE:06:0B:2B:87:0D:46:BB:06:02:11 | ||||
| 	 SHA256: B4:CB:83:B4:DA:D9:9F:99:7D:BE:87:2F:01:3A:A1:6C:14:EE:C4:1D:16:70:21:F3:71:F7:E1:33:0F:27:3E:E6 | ||||
| 	 Signature algorithm name: SHA256withRSA | ||||
| 	 Version: 3 | ||||
| ``` | ||||
|  | ||||
| ## License | ||||
|  | ||||
|     Magisk, including all git submodules are free software: | ||||
|   | ||||
| @@ -1,11 +1,24 @@ | ||||
| apply plugin: 'com.android.application' | ||||
| apply plugin: 'kotlin-android' | ||||
| apply plugin: 'kotlin-android-extensions' | ||||
| apply plugin: 'kotlin-kapt' | ||||
|  | ||||
| kapt { | ||||
|     correctErrorTypes = true | ||||
|     useBuildCache = true | ||||
|     mapDiagnosticLocations = true | ||||
|     javacOptions { | ||||
|         option("-Xmaxerrs", 1000) | ||||
|     } | ||||
| } | ||||
|  | ||||
| android { | ||||
|     defaultConfig { | ||||
|         applicationId 'com.topjohnwu.magisk' | ||||
|         vectorDrawables.useSupportLibrary = true | ||||
|         versionName rootProject.ext.configProps['appVersion'] | ||||
|         versionCode rootProject.ext.configProps['appVersionCode'] as Integer | ||||
|         multiDexEnabled true | ||||
|         versionName configProps['appVersion'] | ||||
|         versionCode configProps['appVersionCode'] as Integer | ||||
|         javaCompileOptions { | ||||
|             annotationProcessorOptions { | ||||
|                 argument('butterknife.debuggable', 'false') | ||||
| @@ -18,6 +31,20 @@ android { | ||||
|             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     dataBinding { | ||||
|         enabled = true | ||||
|     } | ||||
|  | ||||
|     packagingOptions { | ||||
|         exclude '/META-INF/*.version' | ||||
|         exclude '/META-INF/*.kotlin_module' | ||||
|         exclude '/META-INF/rxkotlin.properties' | ||||
|         exclude '/androidsupportmultidexversion.txt' | ||||
|         exclude '/org/**' | ||||
|         exclude '/kotlin/**' | ||||
|         exclude '/kotlinx/**' | ||||
|     } | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
| @@ -25,27 +52,32 @@ dependencies { | ||||
|     implementation project(':net') | ||||
|     implementation project(':shared') | ||||
|     implementation project(':signing') | ||||
|     implementation 'ru.noties:markwon:2.0.1' | ||||
|     implementation 'com.caverock:androidsvg-aar:1.3' | ||||
|     implementation 'org.kamranzafar:jtar:2.3' | ||||
|     implementation 'net.sourceforge.streamsupport:android-retrostreams:1.7.0' | ||||
|     implementation 'com.github.sevar83:indeterminate-checkbox:1.0.5' | ||||
|  | ||||
|     def androidXVersion = "1.0.0" | ||||
|     implementation 'androidx.constraintlayout:constraintlayout:1.1.3' | ||||
|     implementation 'androidx.appcompat:appcompat:1.0.2' | ||||
|     implementation "androidx.preference:preference:${androidXVersion}" | ||||
|     implementation "androidx.recyclerview:recyclerview:${androidXVersion}" | ||||
|     implementation "androidx.cardview:cardview:${androidXVersion}" | ||||
|     implementation "com.google.android.material:material:${androidXVersion}" | ||||
|     implementation 'androidx.work:work-runtime:2.0.0' | ||||
|     implementation 'androidx.transition:transition:1.1.0-alpha02' | ||||
|     implementation 'com.github.topjohnwu:jtar:1.0.0' | ||||
|     implementation 'com.jakewharton.timber:timber:4.7.1' | ||||
|     implementation 'com.github.skoumalcz:teanity:0.3.3' | ||||
|     implementation 'com.ncapdevi:frag-nav:3.2.0' | ||||
|  | ||||
|     def libsuVersion = '2.3.2' | ||||
|     def markwonVersion = '3.0.1' | ||||
|     implementation "ru.noties.markwon:core:${markwonVersion}" | ||||
|     implementation "ru.noties.markwon:html:${markwonVersion}" | ||||
|     implementation "ru.noties.markwon:image-svg:${markwonVersion}" | ||||
|  | ||||
|     def libsuVersion = '2.5.0' | ||||
|     implementation "com.github.topjohnwu.libsu:core:${libsuVersion}" | ||||
|     implementation "com.github.topjohnwu.libsu:io:${libsuVersion}" | ||||
|  | ||||
|     def butterKnifeVersion = '10.1.0' | ||||
|     implementation "com.jakewharton:butterknife-runtime:${butterKnifeVersion}" | ||||
|     annotationProcessor "com.jakewharton:butterknife-compiler:${butterKnifeVersion}" | ||||
|     def koin = "2.0.0-rc-2" | ||||
|     implementation "org.koin:koin-core:${koin}" | ||||
|     implementation "org.koin:koin-android:${koin}" | ||||
|     implementation "org.koin:koin-androidx-viewmodel:${koin}" | ||||
|  | ||||
|     implementation 'androidx.constraintlayout:constraintlayout:1.1.3' | ||||
|     implementation 'androidx.preference:preference:1.0.0' | ||||
|     implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha05' | ||||
|     implementation 'androidx.cardview:cardview:1.0.0' | ||||
|     implementation 'com.google.android.material:material:1.1.0-alpha06' | ||||
|     implementation 'androidx.work:work-runtime:2.0.1' | ||||
|     implementation 'androidx.transition:transition:1.2.0-alpha01' | ||||
|     implementation 'androidx.multidex:multidex:2.0.1' | ||||
| } | ||||
|   | ||||
							
								
								
									
										26
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							| @@ -16,12 +16,6 @@ | ||||
| #   public *; | ||||
| #} | ||||
|  | ||||
| # BouncyCastle | ||||
| -keep,allowoptimization class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; } | ||||
| -keep,allowoptimization class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; } | ||||
| -keep,allowoptimization class org.bouncycastle.jcajce.provider.digest.SHA1** { *; } | ||||
| -dontwarn javax.naming.** | ||||
|  | ||||
| # Snet | ||||
| -keepclassmembers class com.topjohnwu.magisk.utils.ISafetyNetHelper { *; } | ||||
| -keep,allowobfuscation interface com.topjohnwu.magisk.utils.ISafetyNetHelper$Callback | ||||
| @@ -29,15 +23,17 @@ | ||||
|   void onResponse(int); | ||||
| } | ||||
|  | ||||
| # Keep all fragment constructors | ||||
| -keepclassmembers class * extends androidx.fragment.app.Fragment { | ||||
|   public <init>(...); | ||||
| } | ||||
|  | ||||
| # DelegateWorker | ||||
| -keep,allowobfuscation class * extends com.topjohnwu.magisk.model.worker.DelegateWorker | ||||
|  | ||||
| # BootSigner | ||||
| -keepclassmembers class com.topjohnwu.signing.BootSigner { *; } | ||||
|  | ||||
| # SVG | ||||
| -dontwarn com.caverock.androidsvg.SVGAndroidRenderer | ||||
|  | ||||
| # RetroStreams | ||||
| -dontwarn java9.** | ||||
|  | ||||
| # Strip logging | ||||
| -assumenosideeffects class com.topjohnwu.magisk.utils.Logger { | ||||
|   public *** debug(...); | ||||
| @@ -46,4 +42,8 @@ | ||||
| # Excessive obfuscation | ||||
| -repackageclasses 'a' | ||||
| -allowaccessmodification | ||||
| -optimizationpasses 6 | ||||
|  | ||||
| # QOL | ||||
| -dontnote ** | ||||
| -dontwarn com.caverock.androidsvg.** | ||||
| -dontwarn ru.noties.markwon.** | ||||
|   | ||||
| @@ -11,7 +11,7 @@ | ||||
|     <application | ||||
|         android:allowBackup="true" | ||||
|         android:name="a.e" | ||||
|         android:theme="@style/AppTheme" | ||||
|         android:theme="@style/MagiskTheme" | ||||
|         android:usesCleartextTraffic="true" | ||||
|         tools:ignore="UnusedAttribute,GoogleAppIndexingWarning"> | ||||
|  | ||||
| @@ -35,17 +35,16 @@ | ||||
|             android:name="a.f" | ||||
|             android:configChanges="keyboardHidden|orientation|screenSize" | ||||
|             android:screenOrientation="nosensor" | ||||
|             android:theme="@style/AppTheme.NoDrawer" /> | ||||
|             android:theme="@style/MagiskTheme.Flashing" /> | ||||
|  | ||||
|         <!-- Superuser --> | ||||
|  | ||||
|         <activity | ||||
|             android:name="a.m" | ||||
|             android:exported="false" | ||||
|             android:directBootAware="true" | ||||
|             android:excludeFromRecents="true" | ||||
|             android:launchMode="singleTask" | ||||
|             android:taskAffinity="a.m" | ||||
|             android:theme="@style/SuRequest" /> | ||||
|             android:theme="@style/MagiskTheme.SU" /> | ||||
|  | ||||
|         <!-- Receiver --> | ||||
|  | ||||
| @@ -75,4 +74,4 @@ | ||||
|  | ||||
|     </application> | ||||
|  | ||||
| </manifest> | ||||
| </manifest> | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package a; | ||||
|  | ||||
| import com.topjohnwu.magisk.MainActivity; | ||||
| import com.topjohnwu.magisk.ui.MainActivity; | ||||
|  | ||||
| public class b extends MainActivity { | ||||
|     /* stub */ | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package a; | ||||
|  | ||||
| import com.topjohnwu.magisk.SplashActivity; | ||||
| import com.topjohnwu.magisk.ui.SplashActivity; | ||||
|  | ||||
| public class c extends SplashActivity { | ||||
|     /* stub */ | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package a; | ||||
|  | ||||
| import com.topjohnwu.magisk.FlashActivity; | ||||
| import com.topjohnwu.magisk.ui.flash.FlashActivity; | ||||
|  | ||||
| public class f extends FlashActivity { | ||||
|     /* stub */ | ||||
|   | ||||
| @@ -2,7 +2,7 @@ package a; | ||||
|  | ||||
| import android.content.Context; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.UpdateCheckService; | ||||
| import com.topjohnwu.magisk.model.update.UpdateCheckService; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.work.WorkerParameters; | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package a; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.GeneralReceiver; | ||||
| import com.topjohnwu.magisk.model.receiver.GeneralReceiver; | ||||
|  | ||||
| public class h extends GeneralReceiver { | ||||
|     /* stub */ | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package a; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.DownloadModuleService; | ||||
| import com.topjohnwu.magisk.model.download.DownloadModuleService; | ||||
|  | ||||
| public class j extends DownloadModuleService { | ||||
|     /* stub */ | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| package a; | ||||
|  | ||||
| import com.topjohnwu.magisk.SuRequestActivity; | ||||
| import com.topjohnwu.magisk.ui.surequest.SuRequestActivity; | ||||
|  | ||||
| public class m extends SuRequestActivity { | ||||
|     /* stub */ | ||||
|   | ||||
| @@ -2,7 +2,7 @@ package a; | ||||
|  | ||||
| import android.content.Context; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.DelegateWorker; | ||||
| import com.topjohnwu.magisk.model.worker.DelegateWorker; | ||||
|  | ||||
| import java.lang.reflect.ParameterizedType; | ||||
|  | ||||
|   | ||||
| @@ -1,62 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.app.Application; | ||||
| import android.content.Context; | ||||
| import android.content.SharedPreferences; | ||||
| import android.content.res.Configuration; | ||||
| import android.os.AsyncTask; | ||||
| import android.os.Build; | ||||
| import android.preference.PreferenceManager; | ||||
|  | ||||
| import com.topjohnwu.magisk.database.MagiskDB; | ||||
| import com.topjohnwu.magisk.database.RepoDatabaseHelper; | ||||
| import com.topjohnwu.magisk.utils.LocaleManager; | ||||
| import com.topjohnwu.magisk.utils.RootUtils; | ||||
| import com.topjohnwu.net.Networking; | ||||
| import com.topjohnwu.superuser.Shell; | ||||
|  | ||||
| import java.util.concurrent.ThreadPoolExecutor; | ||||
|  | ||||
| public class App extends Application { | ||||
|  | ||||
|     public static App self; | ||||
|     public static Context deContext; | ||||
|     public static ThreadPoolExecutor THREAD_POOL; | ||||
|  | ||||
|     // Global resources | ||||
|     public SharedPreferences prefs; | ||||
|     public MagiskDB mDB; | ||||
|     public RepoDatabaseHelper repoDB; | ||||
|  | ||||
|     static { | ||||
|         Shell.Config.setFlags(Shell.FLAG_MOUNT_MASTER | Shell.FLAG_USE_MAGISK_BUSYBOX); | ||||
|         Shell.Config.verboseLogging(BuildConfig.DEBUG); | ||||
|         Shell.Config.addInitializers(RootUtils.class); | ||||
|         Shell.Config.setTimeout(2); | ||||
|         THREAD_POOL = (ThreadPoolExecutor) AsyncTask.THREAD_POOL_EXECUTOR; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void attachBaseContext(Context base) { | ||||
|         super.attachBaseContext(base); | ||||
|         self = this; | ||||
|         deContext = base; | ||||
|  | ||||
|         if (Build.VERSION.SDK_INT >= 24) { | ||||
|             deContext = base.createDeviceProtectedStorageContext(); | ||||
|             deContext.moveSharedPreferencesFrom(base, | ||||
|                     PreferenceManager.getDefaultSharedPreferencesName(base)); | ||||
|         } | ||||
|         prefs = PreferenceManager.getDefaultSharedPreferences(deContext); | ||||
|         mDB = new MagiskDB(base); | ||||
|  | ||||
|         Networking.init(base); | ||||
|         LocaleManager.setLocale(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onConfigurationChanged(Configuration newConfig) { | ||||
|         super.onConfigurationChanged(newConfig); | ||||
|         LocaleManager.setLocale(this); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										130
									
								
								app/src/main/java/com/topjohnwu/magisk/App.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										130
									
								
								app/src/main/java/com/topjohnwu/magisk/App.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,130 @@ | ||||
| package com.topjohnwu.magisk | ||||
|  | ||||
| import android.annotation.SuppressLint | ||||
| import android.app.Activity | ||||
| import android.app.Application | ||||
| import android.content.Context | ||||
| import android.content.SharedPreferences | ||||
| import android.content.res.Configuration | ||||
| import android.os.AsyncTask | ||||
| import android.os.Build | ||||
| import android.os.Bundle | ||||
| import androidx.appcompat.app.AppCompatDelegate | ||||
| import androidx.multidex.MultiDex | ||||
| import com.topjohnwu.magisk.data.database.MagiskDB | ||||
| import com.topjohnwu.magisk.data.database.RepoDatabaseHelper | ||||
| import com.topjohnwu.magisk.di.koinModules | ||||
| import com.topjohnwu.magisk.utils.LocaleManager | ||||
| import com.topjohnwu.magisk.utils.RootUtils | ||||
| import com.topjohnwu.magisk.utils.inject | ||||
| import com.topjohnwu.net.Networking | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import org.koin.android.ext.android.inject | ||||
| import org.koin.android.ext.koin.androidContext | ||||
| import org.koin.core.context.startKoin | ||||
| import timber.log.Timber | ||||
| import java.util.concurrent.ThreadPoolExecutor | ||||
|  | ||||
| open class App : Application(), Application.ActivityLifecycleCallbacks { | ||||
|  | ||||
|     lateinit var protectedContext: Context | ||||
|  | ||||
|     @Deprecated("Use dependency injection") | ||||
|     val prefs: SharedPreferences by inject() | ||||
|     @Deprecated("Use dependency injection") | ||||
|     val DB: MagiskDB by inject() | ||||
|     @Deprecated("Use dependency injection") | ||||
|     val repoDB: RepoDatabaseHelper by inject() | ||||
|  | ||||
|     @Volatile | ||||
|     private var foreground: Activity? = null | ||||
|  | ||||
|     override fun attachBaseContext(base: Context) { | ||||
|         super.attachBaseContext(base) | ||||
|         if (BuildConfig.DEBUG) | ||||
|             MultiDex.install(base) | ||||
|         Timber.plant(Timber.DebugTree()) | ||||
|  | ||||
|         startKoin { | ||||
|             androidContext(this@App) | ||||
|             modules(koinModules) | ||||
|         } | ||||
|  | ||||
|         protectedContext = baseContext | ||||
|         self = this | ||||
|         deContext = base | ||||
|  | ||||
|         if (Build.VERSION.SDK_INT >= 24) { | ||||
|             protectedContext = base.createDeviceProtectedStorageContext() | ||||
|             deContext = protectedContext | ||||
|             deContext.moveSharedPreferencesFrom(base, base.defaultPrefsName) | ||||
|         } | ||||
|  | ||||
|         registerActivityLifecycleCallbacks(this) | ||||
|  | ||||
|         Networking.init(base) | ||||
|         LocaleManager.setLocale(this) | ||||
|     } | ||||
|  | ||||
|     override fun onConfigurationChanged(newConfig: Configuration) { | ||||
|         super.onConfigurationChanged(newConfig) | ||||
|         LocaleManager.setLocale(this) | ||||
|     } | ||||
|  | ||||
|     //region ActivityLifecycleCallbacks | ||||
|     override fun onActivityCreated(activity: Activity, bundle: Bundle?) {} | ||||
|  | ||||
|     override fun onActivityStarted(activity: Activity) {} | ||||
|  | ||||
|     @Synchronized | ||||
|     override fun onActivityResumed(activity: Activity) { | ||||
|         foreground = activity | ||||
|     } | ||||
|  | ||||
|     @Synchronized | ||||
|     override fun onActivityPaused(activity: Activity) { | ||||
|         foreground = null | ||||
|     } | ||||
|  | ||||
|     override fun onActivityStopped(activity: Activity) {} | ||||
|  | ||||
|     override fun onActivitySaveInstanceState(activity: Activity, bundle: Bundle) {} | ||||
|  | ||||
|     override fun onActivityDestroyed(activity: Activity) {} | ||||
|     //endregion | ||||
|  | ||||
|     private val Context.defaultPrefsName get() = "${packageName}_preferences" | ||||
|  | ||||
|     companion object { | ||||
|  | ||||
|         @SuppressLint("StaticFieldLeak") | ||||
|         @Deprecated("Use dependency injection") | ||||
|         @JvmStatic | ||||
|         lateinit var self: App | ||||
|  | ||||
|         @SuppressLint("StaticFieldLeak") | ||||
|         @Deprecated("Use dependency injection; replace with protectedContext") | ||||
|         @JvmStatic | ||||
|         lateinit var deContext: Context | ||||
|  | ||||
|         @Deprecated("Use Rx or similar") | ||||
|         @JvmField | ||||
|         var THREAD_POOL: ThreadPoolExecutor | ||||
|  | ||||
|         init { | ||||
|             AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) | ||||
|             Shell.Config.setFlags(Shell.FLAG_MOUNT_MASTER or Shell.FLAG_USE_MAGISK_BUSYBOX) | ||||
|             Shell.Config.verboseLogging(BuildConfig.DEBUG) | ||||
|             Shell.Config.addInitializers(RootUtils::class.java) | ||||
|             Shell.Config.setTimeout(2) | ||||
|             THREAD_POOL = AsyncTask.THREAD_POOL_EXECUTOR as ThreadPoolExecutor | ||||
|         } | ||||
|  | ||||
|         @Deprecated("") | ||||
|         @JvmStatic | ||||
|         fun foreground(): Activity? { | ||||
|             val app: App by inject() | ||||
|             return app.foreground | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,8 +1,12 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.DownloadModuleService; | ||||
| import com.topjohnwu.magisk.components.GeneralReceiver; | ||||
| import com.topjohnwu.magisk.components.UpdateCheckService; | ||||
| import com.topjohnwu.magisk.model.download.DownloadModuleService; | ||||
| import com.topjohnwu.magisk.model.receiver.GeneralReceiver; | ||||
| import com.topjohnwu.magisk.model.update.UpdateCheckService; | ||||
| import com.topjohnwu.magisk.ui.MainActivity; | ||||
| import com.topjohnwu.magisk.ui.SplashActivity; | ||||
| import com.topjohnwu.magisk.ui.flash.FlashActivity; | ||||
| import com.topjohnwu.magisk.ui.surequest.SuRequestActivity; | ||||
|  | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|   | ||||
| @@ -3,8 +3,6 @@ package com.topjohnwu.magisk; | ||||
| import android.content.SharedPreferences; | ||||
| import android.util.Xml; | ||||
|  | ||||
| import androidx.collection.ArrayMap; | ||||
|  | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| import com.topjohnwu.superuser.Shell; | ||||
| import com.topjohnwu.superuser.ShellUtils; | ||||
| @@ -17,6 +15,8 @@ import org.xmlpull.v1.XmlPullParserException; | ||||
| import java.io.File; | ||||
| import java.io.IOException; | ||||
|  | ||||
| import androidx.collection.ArrayMap; | ||||
|  | ||||
| public class Config { | ||||
|  | ||||
|     // Current status | ||||
| @@ -59,7 +59,6 @@ public class Config { | ||||
|         public static final String CHECK_UPDATES = "check_update"; | ||||
|         public static final String UPDATE_CHANNEL = "update_channel"; | ||||
|         public static final String CUSTOM_CHANNEL = "custom_channel"; | ||||
|         public static final String BOOT_FORMAT = "boot_format"; | ||||
|         public static final String LOCALE = "locale"; | ||||
|         public static final String DARK_THEME = "dark_theme"; | ||||
|         public static final String ETAG_KEY = "ETag"; | ||||
| @@ -73,6 +72,7 @@ public class Config { | ||||
|     } | ||||
|  | ||||
|     public static class Value { | ||||
|         public static final int DEFAULT_CHANNEL = -1; | ||||
|         public static final int STABLE_CHANNEL = 0; | ||||
|         public static final int BETA_CHANNEL = 1; | ||||
|         public static final int CUSTOM_CHANNEL = 2; | ||||
| @@ -109,16 +109,16 @@ public class Config { | ||||
|     public static void export() { | ||||
|         // Flush prefs to disk | ||||
|         App app = App.self; | ||||
|         app.prefs.edit().commit(); | ||||
|         app.getPrefs().edit().commit(); | ||||
|         File xml = new File(App.deContext.getFilesDir().getParent() + "/shared_prefs", | ||||
|                 app.getPackageName() + "_preferences.xml"); | ||||
|         Shell.su(Utils.fmt("cat %s > /data/adb/%s", xml, Const.MANAGER_CONFIGS)).exec(); | ||||
|     } | ||||
|  | ||||
|     public static void initialize() { | ||||
|         SharedPreferences pref = App.self.prefs; | ||||
|         SharedPreferences pref = App.self.getPrefs(); | ||||
|         SharedPreferences.Editor editor = pref.edit(); | ||||
|         SuFile config = new SuFile("/data/adb/" + Const.MANAGER_CONFIGS); | ||||
|         File config = SuFile.open("/data/adb", Const.MANAGER_CONFIGS); | ||||
|         if (config.exists()) { | ||||
|             try { | ||||
|                 SuFileInputStream is = new SuFileInputStream(config); | ||||
| @@ -213,7 +213,6 @@ public class Config { | ||||
|                 return PREF_BOOL; | ||||
|  | ||||
|             case Key.CUSTOM_CHANNEL: | ||||
|             case Key.BOOT_FORMAT: | ||||
|             case Key.LOCALE: | ||||
|             case Key.ETAG_KEY: | ||||
|                 return PREF_STR; | ||||
| @@ -239,19 +238,19 @@ public class Config { | ||||
|         App app = App.self; | ||||
|         switch (getConfigType(key)) { | ||||
|             case PREF_INT: | ||||
|                 return (T) (Integer) app.prefs.getInt(key, getDef(key)); | ||||
|                 return (T) (Integer) app.getPrefs().getInt(key, getDef(key)); | ||||
|             case PREF_STR_INT: | ||||
|                 return (T) (Integer) Utils.getPrefsInt(app.prefs, key, getDef(key)); | ||||
|                 return (T) (Integer) Utils.getPrefsInt(app.getPrefs(), key, getDef(key)); | ||||
|             case PREF_BOOL: | ||||
|                 return (T) (Boolean) app.prefs.getBoolean(key, getDef(key)); | ||||
|                 return (T) (Boolean) app.getPrefs().getBoolean(key, getDef(key)); | ||||
|             case PREF_STR: | ||||
|                 return (T) app.prefs.getString(key, getDef(key)); | ||||
|                 return (T) app.getPrefs().getString(key, getDef(key)); | ||||
|             case DB_INT: | ||||
|                 return (T) (Integer) app.mDB.getSettings(key, getDef(key)); | ||||
|                 return (T) (Integer) app.getDB().getSettings(key, getDef(key)); | ||||
|             case DB_BOOL: | ||||
|                 return (T) (Boolean) (app.mDB.getSettings(key, getDef(key) ? 1 : 0) != 0); | ||||
|                 return (T) (Boolean) (app.getDB().getSettings(key, getDef(key) ? 1 : 0) != 0); | ||||
|             case DB_STR: | ||||
|                 return (T) app.mDB.getStrings(key, getDef(key)); | ||||
|                 return (T) app.getDB().getStrings(key, getDef(key)); | ||||
|         } | ||||
|         /* Will never get here (IllegalArgumentException in getConfigType) */ | ||||
|         return null; | ||||
| @@ -261,25 +260,25 @@ public class Config { | ||||
|         App app = App.self; | ||||
|         switch (getConfigType(key)) { | ||||
|             case PREF_INT: | ||||
|                 app.prefs.edit().putInt(key, (int) val).apply(); | ||||
|                 app.getPrefs().edit().putInt(key, (int) val).apply(); | ||||
|                 break; | ||||
|             case PREF_STR_INT: | ||||
|                 app.prefs.edit().putString(key, String.valueOf(val)).apply(); | ||||
|                 app.getPrefs().edit().putString(key, String.valueOf(val)).apply(); | ||||
|                 break; | ||||
|             case PREF_BOOL: | ||||
|                 app.prefs.edit().putBoolean(key, (boolean) val).apply(); | ||||
|                 app.getPrefs().edit().putBoolean(key, (boolean) val).apply(); | ||||
|                 break; | ||||
|             case PREF_STR: | ||||
|                 app.prefs.edit().putString(key, (String) val).apply(); | ||||
|                 app.getPrefs().edit().putString(key, (String) val).apply(); | ||||
|                 break; | ||||
|             case DB_INT: | ||||
|                 app.mDB.setSettings(key, (int) val); | ||||
|                 app.getDB().setSettings(key, (int) val); | ||||
|                 break; | ||||
|             case DB_BOOL: | ||||
|                 app.mDB.setSettings(key, (boolean) val ? 1 : 0); | ||||
|                 app.getDB().setSettings(key, (boolean) val ? 1 : 0); | ||||
|                 break; | ||||
|             case DB_STR: | ||||
|                 app.mDB.setStrings(key, (String) val); | ||||
|                 app.getDB().setStrings(key, (String) val); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| @@ -291,14 +290,14 @@ public class Config { | ||||
|             case PREF_STR_INT: | ||||
|             case PREF_BOOL: | ||||
|             case PREF_STR: | ||||
|                 app.prefs.edit().remove(key).apply(); | ||||
|                 app.getPrefs().edit().remove(key).apply(); | ||||
|                 break; | ||||
|             case DB_BOOL: | ||||
|             case DB_INT: | ||||
|                 app.mDB.rmSettings(key); | ||||
|                 app.getDB().rmSettings(key); | ||||
|                 break; | ||||
|             case DB_STR: | ||||
|                 app.mDB.setStrings(key, null); | ||||
|                 app.getDB().setStrings(key, null); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
| @@ -316,7 +315,7 @@ public class Config { | ||||
|         defs.put(Key.SU_AUTO_RESPONSE, Value.SU_PROMPT); | ||||
|         defs.put(Key.SU_NOTIFICATION, Value.NOTIFICATION_TOAST); | ||||
|         defs.put(Key.UPDATE_CHANNEL, Utils.isCanary() ? | ||||
|                 Value.CANARY_DEBUG_CHANNEL : Value.STABLE_CHANNEL); | ||||
|                 Value.CANARY_DEBUG_CHANNEL : Value.DEFAULT_CHANNEL); | ||||
|  | ||||
|         // prefs bool | ||||
|         defs.put(Key.CHECK_UPDATES, true); | ||||
| @@ -326,7 +325,6 @@ public class Config { | ||||
|  | ||||
|         // prefs string | ||||
|         defs.put(Key.CUSTOM_CHANNEL, ""); | ||||
|         defs.put(Key.BOOT_FORMAT, ".img"); | ||||
|         defs.put(Key.LOCALE, ""); | ||||
|         //defs.put(Key.ETAG_KEY, null); | ||||
|  | ||||
| @@ -367,13 +365,13 @@ public class Config { | ||||
|             switch (type) { | ||||
|                 case DB_INT: | ||||
|                     editor.putString(key, String.valueOf( | ||||
|                             app.mDB.getSettings(key, (Integer) defs.get(key)))); | ||||
|                             app.getDB().getSettings(key, (Integer) defs.get(key)))); | ||||
|                     continue; | ||||
|                 case DB_STR: | ||||
|                     editor.putString(key, app.mDB.getStrings(key, (String) defs.get(key))); | ||||
|                     editor.putString(key, app.getDB().getStrings(key, (String) defs.get(key))); | ||||
|                     continue; | ||||
|                 case DB_BOOL: | ||||
|                     int bs = app.mDB.getSettings(key, -1); | ||||
|                     int bs = app.getDB().getSettings(key, -1); | ||||
|                     editor.putBoolean(key, bs < 0 ? (Boolean) defs.get(key) : bs != 0); | ||||
|                     continue; | ||||
|             } | ||||
|   | ||||
| @@ -25,27 +25,26 @@ public class Const { | ||||
|         EXTERNAL_PATH.mkdirs(); | ||||
|     } | ||||
|  | ||||
|     public static final String BUSYBOX_PATH = "/sbin/.magisk/busybox"; | ||||
|     public static final String TMP_FOLDER_PATH = "/dev/tmp"; | ||||
|     public static final String MAGISK_LOG = "/cache/magisk.log"; | ||||
|     public static final String MANAGER_CONFIGS = ".tmp.magisk.config"; | ||||
|  | ||||
|     // Versions | ||||
|     public static final int UPDATE_SERVICE_VER = 1; | ||||
|     public static final int MIN_MODULE_VER = 1500; | ||||
|     public static final int SNET_EXT_VER = 12; | ||||
|  | ||||
|     public static final int USER_ID = Process.myUid() / 100000; | ||||
|  | ||||
|     // Generic | ||||
|     public static final String MAGISK_INSTALL_LOG_FILENAME = "magisk_install_log_%s.log"; | ||||
|  | ||||
|     public static final class MAGISK_VER { | ||||
|         public static final int MIN_SUPPORT = 18000; | ||||
|     } | ||||
|  | ||||
|     public static class ID { | ||||
|         public static final int UPDATE_SERVICE_ID = 1; | ||||
|         public static final int FETCH_ZIP = 2; | ||||
|         public static final int SELECT_BOOT = 3; | ||||
|         public static final int ONBOOT_SERVICE_ID = 6; | ||||
|  | ||||
|         // notifications | ||||
|         public static final int MAGISK_UPDATE_NOTIFICATION_ID = 4; | ||||
| @@ -83,19 +82,17 @@ public class Const { | ||||
|         public static final String LINK_KEY = "Link"; | ||||
|         public static final String IF_NONE_MATCH = "If-None-Match"; | ||||
|         // intents | ||||
|         public static final String FROM_SPLASH = "splash"; | ||||
|         public static final String OPEN_SECTION = "section"; | ||||
|         public static final String INTENT_SET_NAME = "filename"; | ||||
|         public static final String INTENT_SET_LINK = "link"; | ||||
|         public static final String FLASH_ACTION = "action"; | ||||
|         public static final String FLASH_SET_BOOT = "boot"; | ||||
|         public static final String BROADCAST_MANAGER_UPDATE = "manager_update"; | ||||
|         public static final String BROADCAST_REBOOT = "reboot"; | ||||
|     } | ||||
|  | ||||
|     public static class Value { | ||||
|         public static final String FLASH_ZIP = "flash"; | ||||
|         public static final String PATCH_BOOT = "patch"; | ||||
|         public static final String PATCH_FILE = "patch"; | ||||
|         public static final String FLASH_MAGISK = "magisk"; | ||||
|         public static final String FLASH_INACTIVE_SLOT = "slot"; | ||||
|         public static final String UNINSTALL = "uninstall"; | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,190 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.content.Intent; | ||||
| import android.os.Build; | ||||
| import android.os.Bundle; | ||||
| import android.os.Handler; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.appcompat.app.ActionBarDrawerToggle; | ||||
| import androidx.appcompat.widget.Toolbar; | ||||
| import androidx.drawerlayout.widget.DrawerLayout; | ||||
| import androidx.fragment.app.Fragment; | ||||
| import androidx.fragment.app.FragmentTransaction; | ||||
|  | ||||
| import com.google.android.material.navigation.NavigationView; | ||||
| import com.topjohnwu.magisk.components.BaseActivity; | ||||
| import com.topjohnwu.magisk.fragments.LogFragment; | ||||
| import com.topjohnwu.magisk.fragments.MagiskFragment; | ||||
| import com.topjohnwu.magisk.fragments.MagiskHideFragment; | ||||
| import com.topjohnwu.magisk.fragments.ModulesFragment; | ||||
| import com.topjohnwu.magisk.fragments.ReposFragment; | ||||
| import com.topjohnwu.magisk.fragments.SettingsFragment; | ||||
| import com.topjohnwu.magisk.fragments.SuperuserFragment; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| import com.topjohnwu.net.Networking; | ||||
| import com.topjohnwu.superuser.Shell; | ||||
|  | ||||
| import butterknife.BindView; | ||||
|  | ||||
| public class MainActivity extends BaseActivity | ||||
|         implements NavigationView.OnNavigationItemSelectedListener { | ||||
|  | ||||
|     private final Handler mDrawerHandler = new Handler(); | ||||
|     private int mDrawerItem; | ||||
|     private static boolean fromShortcut = false; | ||||
|  | ||||
|     @BindView(R.id.toolbar) public Toolbar toolbar; | ||||
|     @BindView(R.id.drawer_layout) DrawerLayout drawer; | ||||
|     @BindView(R.id.nav_view) NavigationView navigationView; | ||||
|  | ||||
|     private float toolbarElevation; | ||||
|  | ||||
|     @Override | ||||
|     public int getDarkTheme() { | ||||
|         return R.style.AppTheme_Dark; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(final Bundle savedInstanceState) { | ||||
|         if (!getIntent().getBooleanExtra(Const.Key.FROM_SPLASH, false)) { | ||||
|             startActivity(new Intent(this, ClassMap.get(SplashActivity.class))); | ||||
|             finish(); | ||||
|         } | ||||
|  | ||||
|         super.onCreate(savedInstanceState); | ||||
|         setContentView(R.layout.activity_main); | ||||
|         new MainActivity_ViewBinding(this); | ||||
|         checkHideSection(); | ||||
|         setSupportActionBar(toolbar); | ||||
|  | ||||
|         ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar, R.string.magisk, R.string.magisk) { | ||||
|             @Override | ||||
|             public void onDrawerOpened(View drawerView) { | ||||
|                 super.onDrawerOpened(drawerView); | ||||
|                 super.onDrawerSlide(drawerView, 0); // this disables the arrow @ completed tate | ||||
|             } | ||||
|  | ||||
|             @Override | ||||
|             public void onDrawerSlide(View drawerView, float slideOffset) { | ||||
|                 super.onDrawerSlide(drawerView, 0); // this disables the animation | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | ||||
|             toolbarElevation = toolbar.getElevation(); | ||||
|         } | ||||
|  | ||||
|         drawer.addDrawerListener(toggle); | ||||
|         toggle.syncState(); | ||||
|  | ||||
|         if (savedInstanceState == null) { | ||||
|             String section = getIntent().getStringExtra(Const.Key.OPEN_SECTION); | ||||
|             fromShortcut = section != null; | ||||
|             navigate(section); | ||||
|         } | ||||
|  | ||||
|         navigationView.setNavigationItemSelectedListener(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBackPressed() { | ||||
|         if (drawer.isDrawerOpen(navigationView)) { | ||||
|             drawer.closeDrawer(navigationView); | ||||
|         } else if (mDrawerItem != R.id.magisk && !fromShortcut) { | ||||
|             navigate(R.id.magisk); | ||||
|         } else { | ||||
|             finish(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onNavigationItemSelected(@NonNull final MenuItem menuItem) { | ||||
|         mDrawerHandler.removeCallbacksAndMessages(null); | ||||
|         mDrawerHandler.postDelayed(() -> navigate(menuItem.getItemId()), 250); | ||||
|         drawer.closeDrawer(navigationView); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     public void checkHideSection() { | ||||
|         Menu menu = navigationView.getMenu(); | ||||
|         menu.findItem(R.id.magiskhide).setVisible(Shell.rootAccess() && | ||||
|                 (boolean) Config.get(Config.Key.MAGISKHIDE)); | ||||
|         menu.findItem(R.id.modules).setVisible(Shell.rootAccess() && Config.magiskVersionCode >= 0); | ||||
|         menu.findItem(R.id.downloads).setVisible(Networking.checkNetworkStatus(this) | ||||
|                 && Shell.rootAccess() && Config.magiskVersionCode >= 0); | ||||
|         menu.findItem(R.id.log).setVisible(Shell.rootAccess()); | ||||
|         menu.findItem(R.id.superuser).setVisible(Utils.showSuperUser()); | ||||
|     } | ||||
|  | ||||
|     public void navigate(String item) { | ||||
|         int itemId = R.id.magisk; | ||||
|         if (item != null) { | ||||
|             switch (item) { | ||||
|                 case "superuser": | ||||
|                     itemId = R.id.superuser; | ||||
|                     break; | ||||
|                 case "modules": | ||||
|                     itemId = R.id.modules; | ||||
|                     break; | ||||
|                 case "downloads": | ||||
|                     itemId = R.id.downloads; | ||||
|                     break; | ||||
|                 case "magiskhide": | ||||
|                     itemId = R.id.magiskhide; | ||||
|                     break; | ||||
|                 case "log": | ||||
|                     itemId = R.id.log; | ||||
|                     break; | ||||
|                 case "settings": | ||||
|                     itemId = R.id.settings; | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|         navigate(itemId); | ||||
|     } | ||||
|  | ||||
|     public void navigate(int itemId) { | ||||
|         mDrawerItem = itemId; | ||||
|         navigationView.setCheckedItem(itemId); | ||||
|         switch (itemId) { | ||||
|             case R.id.magisk: | ||||
|                 fromShortcut = false; | ||||
|                 displayFragment(new MagiskFragment(), true); | ||||
|                 break; | ||||
|             case R.id.superuser: | ||||
|                 displayFragment(new SuperuserFragment(), true); | ||||
|                 break; | ||||
|             case R.id.modules: | ||||
|                 displayFragment(new ModulesFragment(), true); | ||||
|                 break; | ||||
|             case R.id.downloads: | ||||
|                 displayFragment(new ReposFragment(), true); | ||||
|                 break; | ||||
|             case R.id.magiskhide: | ||||
|                 displayFragment(new MagiskHideFragment(), true); | ||||
|                 break; | ||||
|             case R.id.log: | ||||
|                 displayFragment(new LogFragment(), false); | ||||
|                 break; | ||||
|             case R.id.settings: | ||||
|                 displayFragment(new SettingsFragment(), true); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void displayFragment(@NonNull Fragment navFragment, boolean setElevation) { | ||||
|         supportInvalidateOptionsMenu(); | ||||
|         getSupportFragmentManager() | ||||
|                 .beginTransaction() | ||||
|                 .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN) | ||||
|                 .replace(R.id.content_frame, navFragment) | ||||
|                 .commitNow(); | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { | ||||
|             toolbar.setElevation(setElevation ? toolbarElevation : 0); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,89 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.content.Intent; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.os.Bundle; | ||||
| import android.text.TextUtils; | ||||
|  | ||||
| import androidx.appcompat.app.AlertDialog; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.BaseActivity; | ||||
| import com.topjohnwu.magisk.database.RepoDatabaseHelper; | ||||
| import com.topjohnwu.magisk.tasks.UpdateRepos; | ||||
| import com.topjohnwu.magisk.uicomponents.Notifications; | ||||
| import com.topjohnwu.magisk.uicomponents.Shortcuts; | ||||
| import com.topjohnwu.magisk.utils.LocaleManager; | ||||
| import com.topjohnwu.magisk.utils.Utils; | ||||
| import com.topjohnwu.net.Networking; | ||||
| import com.topjohnwu.superuser.Shell; | ||||
|  | ||||
| public class SplashActivity extends BaseActivity { | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|  | ||||
|         Shell.getShell(shell -> { | ||||
|             if (Config.magiskVersionCode > 0 && | ||||
|                     Config.magiskVersionCode < Const.MAGISK_VER.MIN_SUPPORT) { | ||||
|                 new AlertDialog.Builder(this) | ||||
|                         .setTitle(R.string.unsupport_magisk_title) | ||||
|                         .setMessage(R.string.unsupport_magisk_message) | ||||
|                         .setNegativeButton(R.string.ok, null) | ||||
|                         .setOnDismissListener(dialog -> finish()) | ||||
|                         .show(); | ||||
|             } else { | ||||
|                 initAndStart(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private void initAndStart() { | ||||
|         String pkg = Config.get(Config.Key.SU_MANAGER); | ||||
|         if (pkg != null && getPackageName().equals(BuildConfig.APPLICATION_ID)) { | ||||
|             Config.remove(Config.Key.SU_MANAGER); | ||||
|             Shell.su("pm uninstall " + pkg).submit(); | ||||
|         } | ||||
|         if (TextUtils.equals(pkg, getPackageName())) { | ||||
|             try { | ||||
|                 // We are the manager, remove com.topjohnwu.magisk as it could be malware | ||||
|                 getPackageManager().getApplicationInfo(BuildConfig.APPLICATION_ID, 0); | ||||
|                 Shell.su("pm uninstall " + BuildConfig.APPLICATION_ID).submit(); | ||||
|             } catch (PackageManager.NameNotFoundException ignored) {} | ||||
|         } | ||||
|  | ||||
|         // Dynamic detect all locales | ||||
|         LocaleManager.loadAvailableLocales(R.string.app_changelog); | ||||
|  | ||||
|         // Set default configs | ||||
|         Config.initialize(); | ||||
|  | ||||
|         // Create notification channel on Android O | ||||
|         Notifications.setup(this); | ||||
|  | ||||
|         // Schedule periodic update checks | ||||
|         Utils.scheduleUpdateCheck(); | ||||
|  | ||||
|         // Setup shortcuts | ||||
|         Shortcuts.setup(this); | ||||
|  | ||||
|         // Create repo database | ||||
|         app.repoDB = new RepoDatabaseHelper(this); | ||||
|  | ||||
|         // Magisk working as expected | ||||
|         if (Shell.rootAccess() && Config.magiskVersionCode > 0) { | ||||
|             // Load modules | ||||
|             Utils.loadModules(false); | ||||
|             // Load repos | ||||
|             if (Networking.checkNetworkStatus(this)) | ||||
|                 new UpdateRepos().exec(); | ||||
|         } | ||||
|  | ||||
|         Intent intent = new Intent(this, ClassMap.get(MainActivity.class)); | ||||
|         intent.putExtra(Const.Key.OPEN_SECTION, getIntent().getStringExtra(Const.Key.OPEN_SECTION)); | ||||
|         intent.putExtra(Const.Key.FROM_SPLASH, true); | ||||
|         intent.putExtra(BaseActivity.INTENT_PERM, getIntent().getStringExtra(BaseActivity.INTENT_PERM)); | ||||
|         startActivity(intent); | ||||
|         finish(); | ||||
|     } | ||||
| } | ||||
| @@ -1,229 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.content.Intent; | ||||
| import android.content.SharedPreferences; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.hardware.fingerprint.FingerprintManager; | ||||
| import android.os.Bundle; | ||||
| import android.os.CountDownTimer; | ||||
| import android.text.TextUtils; | ||||
| import android.view.View; | ||||
| import android.view.Window; | ||||
| import android.widget.ArrayAdapter; | ||||
| import android.widget.Button; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.LinearLayout; | ||||
| import android.widget.Spinner; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.appcompat.content.res.AppCompatResources; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.BaseActivity; | ||||
| import com.topjohnwu.magisk.container.Policy; | ||||
| import com.topjohnwu.magisk.utils.FingerprintHelper; | ||||
| import com.topjohnwu.magisk.utils.SuConnector; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| import butterknife.BindView; | ||||
|  | ||||
| public class SuRequestActivity extends BaseActivity { | ||||
|     @BindView(R.id.su_popup) LinearLayout suPopup; | ||||
|     @BindView(R.id.timeout) Spinner timeout; | ||||
|     @BindView(R.id.app_icon) ImageView appIcon; | ||||
|     @BindView(R.id.app_name) TextView appNameView; | ||||
|     @BindView(R.id.package_name) TextView packageNameView; | ||||
|     @BindView(R.id.grant_btn) Button grant_btn; | ||||
|     @BindView(R.id.deny_btn) Button deny_btn; | ||||
|     @BindView(R.id.fingerprint) ImageView fingerprintImg; | ||||
|     @BindView(R.id.warning) TextView warning; | ||||
|  | ||||
|     private SuConnector connector; | ||||
|     private Policy policy; | ||||
|     private CountDownTimer timer; | ||||
|     private FingerprintHelper fingerprintHelper; | ||||
|     private SharedPreferences timeoutPrefs; | ||||
|  | ||||
|     @Override | ||||
|     public int getDarkTheme() { | ||||
|         return R.style.SuRequest_Dark; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void finish() { | ||||
|         if (timer != null) | ||||
|             timer.cancel(); | ||||
|         if (fingerprintHelper != null) | ||||
|             fingerprintHelper.cancel(); | ||||
|         super.finish(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBackPressed() { | ||||
|         if (policy != null) { | ||||
|             handleAction(Policy.DENY); | ||||
|         } else { | ||||
|             finish(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         super.onCreate(savedInstanceState); | ||||
|         supportRequestWindowFeature(Window.FEATURE_NO_TITLE); | ||||
|  | ||||
|         PackageManager pm = getPackageManager(); | ||||
|         app.mDB.clearOutdated(); | ||||
|         timeoutPrefs = App.deContext.getSharedPreferences("su_timeout", 0); | ||||
|  | ||||
|         // Get policy | ||||
|         Intent intent = getIntent(); | ||||
|         try { | ||||
|             String socketName = intent.getStringExtra("socket"); | ||||
|             connector = new SuConnector(socketName) { | ||||
|                 @Override | ||||
|                 protected void onResponse() throws IOException { | ||||
|                     out.writeInt(policy.policy); | ||||
|                 } | ||||
|             }; | ||||
|             Bundle bundle = connector.readSocketInput(); | ||||
|             int uid = Integer.parseInt(bundle.getString("uid")); | ||||
|             policy = app.mDB.getPolicy(uid); | ||||
|             if (policy == null) { | ||||
|                 policy = new Policy(uid, pm); | ||||
|             } | ||||
|         } catch (IOException | PackageManager.NameNotFoundException e) { | ||||
|             e.printStackTrace(); | ||||
|             finish(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Never allow com.topjohnwu.magisk (could be malware) | ||||
|         if (TextUtils.equals(policy.packageName, BuildConfig.APPLICATION_ID)) { | ||||
|             finish(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         switch ((int) Config.get(Config.Key.SU_AUTO_RESPONSE)) { | ||||
|             case Config.Value.SU_AUTO_DENY: | ||||
|                 handleAction(Policy.DENY, 0); | ||||
|                 return; | ||||
|             case Config.Value.SU_AUTO_ALLOW: | ||||
|                 handleAction(Policy.ALLOW, 0); | ||||
|                 return; | ||||
|             case Config.Value.SU_PROMPT: | ||||
|             default: | ||||
|         } | ||||
|  | ||||
|         // If not interactive, response directly | ||||
|         if (policy.policy != Policy.INTERACTIVE) { | ||||
|             handleAction(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         setContentView(R.layout.activity_request); | ||||
|         new SuRequestActivity_ViewBinding(this); | ||||
|  | ||||
|         appIcon.setImageDrawable(policy.info.loadIcon(pm)); | ||||
|         appNameView.setText(policy.appName); | ||||
|         packageNameView.setText(policy.packageName); | ||||
|         warning.setCompoundDrawablesRelativeWithIntrinsicBounds( | ||||
|                 AppCompatResources.getDrawable(this, R.drawable.ic_warning), null, null, null); | ||||
|  | ||||
|         ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, | ||||
|                 R.array.allow_timeout, android.R.layout.simple_spinner_item); | ||||
|         adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); | ||||
|         timeout.setAdapter(adapter); | ||||
|         timeout.setSelection(timeoutPrefs.getInt(policy.packageName, 0)); | ||||
|  | ||||
|         timer = new CountDownTimer((int) Config.get(Config.Key.SU_REQUEST_TIMEOUT) * 1000, 1000) { | ||||
|             @Override | ||||
|             public void onTick(long millisUntilFinished) { | ||||
|                 deny_btn.setText(getString(R.string.deny_with_str, "(" + millisUntilFinished / 1000 + ")")); | ||||
|             } | ||||
|             @Override | ||||
|             public void onFinish() { | ||||
|                 deny_btn.setText(getString(R.string.deny_with_str, "(0)")); | ||||
|                 handleAction(Policy.DENY); | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         boolean useFP = FingerprintHelper.useFingerprint(); | ||||
|  | ||||
|         if (useFP) { | ||||
|             try { | ||||
|                 fingerprintHelper = new FingerprintHelper() { | ||||
|                     @Override | ||||
|                     public void onAuthenticationError(int errorCode, CharSequence errString) { | ||||
|                         warning.setText(errString); | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     public void onAuthenticationHelp(int helpCode, CharSequence helpString) { | ||||
|                         warning.setText(helpString); | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) { | ||||
|                         handleAction(Policy.ALLOW); | ||||
|                     } | ||||
|  | ||||
|                     @Override | ||||
|                     public void onAuthenticationFailed() { | ||||
|                         warning.setText(R.string.auth_fail); | ||||
|                     } | ||||
|                 }; | ||||
|                 fingerprintHelper.authenticate(); | ||||
|             } catch (Exception e) { | ||||
|                 e.printStackTrace(); | ||||
|                 useFP = false; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (!useFP) { | ||||
|             grant_btn.setOnClickListener(v -> { | ||||
|                 handleAction(Policy.ALLOW); | ||||
|                 timer.cancel(); | ||||
|             }); | ||||
|             grant_btn.requestFocus(); | ||||
|         } | ||||
|  | ||||
|         grant_btn.setVisibility(useFP ? View.GONE : View.VISIBLE); | ||||
|         fingerprintImg.setVisibility(useFP ? View.VISIBLE : View.GONE); | ||||
|  | ||||
|         deny_btn.setOnClickListener(v -> { | ||||
|             handleAction(Policy.DENY); | ||||
|             timer.cancel(); | ||||
|         }); | ||||
|         suPopup.setOnClickListener(v -> cancelTimeout()); | ||||
|         timeout.setOnTouchListener((v, event) -> cancelTimeout()); | ||||
|         timer.start(); | ||||
|     } | ||||
|  | ||||
|     private boolean cancelTimeout() { | ||||
|         timer.cancel(); | ||||
|         deny_btn.setText(getString(R.string.deny)); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     private void handleAction() { | ||||
|         connector.response(); | ||||
|         finish(); | ||||
|     } | ||||
|  | ||||
|     private void handleAction(int action) { | ||||
|         int pos = timeout.getSelectedItemPosition(); | ||||
|         timeoutPrefs.edit().putInt(policy.packageName, pos).apply(); | ||||
|         handleAction(action, Config.Value.TIMEOUT_LIST[pos]); | ||||
|     } | ||||
|  | ||||
|     private void handleAction(int action, int time) { | ||||
|         policy.policy = action; | ||||
|         if (time >= 0) { | ||||
|             policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60); | ||||
|             app.mDB.updatePolicy(policy); | ||||
|         } | ||||
|         handleAction(); | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,127 +0,0 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.text.TextUtils; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.CheckBox; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
|  | ||||
| import com.google.android.material.snackbar.Snackbar; | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.container.Module; | ||||
| import com.topjohnwu.magisk.uicomponents.SnackbarMaker; | ||||
| import com.topjohnwu.superuser.Shell; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import butterknife.BindView; | ||||
|  | ||||
| public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHolder> { | ||||
|  | ||||
|     private final List<Module> mList; | ||||
|  | ||||
|     public ModulesAdapter(List<Module> list) { | ||||
|         mList = list; | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { | ||||
|         View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false); | ||||
|         return new ViewHolder(view); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBindViewHolder(final ViewHolder holder, int position) { | ||||
|         Context context = holder.itemView.getContext(); | ||||
|         final Module module = mList.get(position); | ||||
|  | ||||
|         String version = module.getVersion(); | ||||
|         String author = module.getAuthor(); | ||||
|         String description = module.getDescription(); | ||||
|         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.setOnCheckedChangeListener((v, isChecked) -> { | ||||
|             int snack; | ||||
|             if (isChecked) { | ||||
|                 module.removeDisableFile(); | ||||
|                 snack = R.string.disable_file_removed; | ||||
|             } else { | ||||
|                 module.createDisableFile(); | ||||
|                 snack = R.string.disable_file_created; | ||||
|             } | ||||
|             SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); | ||||
|         }); | ||||
|  | ||||
|         holder.delete.setOnClickListener(v -> { | ||||
|             boolean removed = module.willBeRemoved(); | ||||
|             int snack; | ||||
|             if (removed) { | ||||
|                 module.deleteRemoveFile(); | ||||
|                 snack = R.string.remove_file_deleted; | ||||
|             } else { | ||||
|                 module.createRemoveFile(); | ||||
|                 snack = R.string.remove_file_created; | ||||
|             } | ||||
|             SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show(); | ||||
|             updateDeleteButton(holder, module); | ||||
|         }); | ||||
|  | ||||
|         if (module.isUpdated()) { | ||||
|             holder.notice.setVisibility(View.VISIBLE); | ||||
|             holder.notice.setText(R.string.update_file_created); | ||||
|             holder.delete.setEnabled(false); | ||||
|         } else { | ||||
|             updateDeleteButton(holder, module); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private void updateDeleteButton(ViewHolder holder, Module module) { | ||||
|         holder.notice.setVisibility(module.willBeRemoved() ? View.VISIBLE : View.GONE); | ||||
|  | ||||
|         if (module.willBeRemoved()) { | ||||
|             holder.delete.setImageResource(R.drawable.ic_undelete); | ||||
|         } else { | ||||
|             holder.delete.setImageResource(R.drawable.ic_delete); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getItemCount() { | ||||
|         return mList.size(); | ||||
|     } | ||||
|  | ||||
|     static class ViewHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         @BindView(R.id.title) TextView title; | ||||
|         @BindView(R.id.version_name) TextView versionName; | ||||
|         @BindView(R.id.description) TextView description; | ||||
|         @BindView(R.id.notice) TextView notice; | ||||
|         @BindView(R.id.checkbox) CheckBox checkBox; | ||||
|         @BindView(R.id.author) TextView author; | ||||
|         @BindView(R.id.delete) ImageView delete; | ||||
|  | ||||
|         ViewHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             new ModulesAdapter$ViewHolder_ViewBinding(this, itemView); | ||||
|  | ||||
|             if (!Shell.rootAccess()) { | ||||
|                 checkBox.setEnabled(false); | ||||
|                 delete.setEnabled(false); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,172 +0,0 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.content.DialogInterface; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.appcompat.widget.SwitchCompat; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
|  | ||||
| import com.google.android.material.snackbar.Snackbar; | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.container.Policy; | ||||
| import com.topjohnwu.magisk.database.MagiskDB; | ||||
| import com.topjohnwu.magisk.dialogs.CustomAlertDialog; | ||||
| import com.topjohnwu.magisk.dialogs.FingerprintAuthDialog; | ||||
| import com.topjohnwu.magisk.uicomponents.ArrowExpandable; | ||||
| import com.topjohnwu.magisk.uicomponents.Expandable; | ||||
| import com.topjohnwu.magisk.uicomponents.ExpandableViewHolder; | ||||
| import com.topjohnwu.magisk.uicomponents.SnackbarMaker; | ||||
| import com.topjohnwu.magisk.utils.FingerprintHelper; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| import butterknife.BindView; | ||||
|  | ||||
| public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> { | ||||
|  | ||||
|     private List<Policy> policyList; | ||||
|     private MagiskDB dbHelper; | ||||
|     private PackageManager pm; | ||||
|     private boolean[] expandList; | ||||
|  | ||||
|     public PolicyAdapter(List<Policy> list, MagiskDB db, PackageManager pm) { | ||||
|         policyList = list; | ||||
|         expandList = new boolean[policyList.size()]; | ||||
|         dbHelper = db; | ||||
|         this.pm = pm; | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public ViewHolder onCreateViewHolder(@NonNull 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(@NonNull ViewHolder holder, int position) { | ||||
|         Policy policy = policyList.get(position); | ||||
|  | ||||
|         holder.settings.setExpanded(expandList[position]); | ||||
|         holder.trigger.setOnClickListener(view -> { | ||||
|             if (holder.settings.isExpanded()) { | ||||
|                 holder.settings.collapse(); | ||||
|                 expandList[position] = false; | ||||
|             } else { | ||||
|                 holder.settings.expand(); | ||||
|                 expandList[position] = true; | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         holder.appName.setText(policy.appName); | ||||
|         holder.packageName.setText(policy.packageName); | ||||
|         holder.appIcon.setImageDrawable(policy.info.loadIcon(pm)); | ||||
|  | ||||
|         holder.notificationSwitch.setOnCheckedChangeListener(null); | ||||
|         holder.loggingSwitch.setOnCheckedChangeListener(null); | ||||
|  | ||||
|         holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW); | ||||
|         holder.notificationSwitch.setChecked(policy.notification); | ||||
|         holder.loggingSwitch.setChecked(policy.logging); | ||||
|  | ||||
|         holder.masterSwitch.setOnClickListener(v -> { | ||||
|             boolean isChecked = holder.masterSwitch.isChecked(); | ||||
|             Runnable r = () -> { | ||||
|                 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); | ||||
|                 } | ||||
|             }; | ||||
|             if (FingerprintHelper.useFingerprint()) { | ||||
|                 holder.masterSwitch.setChecked(!isChecked); | ||||
|                 new FingerprintAuthDialog((Activity) v.getContext(), () -> { | ||||
|                     holder.masterSwitch.setChecked(isChecked); | ||||
|                     r.run(); | ||||
|                 }).show(); | ||||
|             } else { | ||||
|                 r.run(); | ||||
|             } | ||||
|         }); | ||||
|         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 -> { | ||||
|             DialogInterface.OnClickListener l = (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); | ||||
|             }; | ||||
|             if (FingerprintHelper.useFingerprint()) { | ||||
|                 new FingerprintAuthDialog((Activity) v.getContext(), | ||||
|                         () -> l.onClick(null, 0)).show(); | ||||
|             } else { | ||||
|                 new CustomAlertDialog((Activity) v.getContext()) | ||||
|                         .setTitle(R.string.su_revoke_title) | ||||
|                         .setMessage(v.getContext().getString(R.string.su_revoke_msg, policy.appName)) | ||||
|                         .setPositiveButton(R.string.yes, l) | ||||
|                         .setNegativeButton(R.string.no_thanks, null) | ||||
|                         .setCancelable(true) | ||||
|                         .show(); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getItemCount() { | ||||
|         return policyList.size(); | ||||
|     } | ||||
|  | ||||
|     static class ViewHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         @BindView(R.id.app_name) TextView appName; | ||||
|         @BindView(R.id.package_name) TextView packageName; | ||||
|         @BindView(R.id.app_icon) ImageView appIcon; | ||||
|         @BindView(R.id.master_switch) SwitchCompat masterSwitch; | ||||
|         @BindView(R.id.notification_switch) SwitchCompat notificationSwitch; | ||||
|         @BindView(R.id.logging_switch) SwitchCompat loggingSwitch; | ||||
|         @BindView(R.id.expand_layout) ViewGroup expandLayout; | ||||
|         @BindView(R.id.arrow) ImageView arrow; | ||||
|         @BindView(R.id.trigger) View trigger; | ||||
|         @BindView(R.id.delete) ImageView delete; | ||||
|         @BindView(R.id.more_info) ImageView moreInfo; | ||||
|  | ||||
|         Expandable settings; | ||||
|  | ||||
|         public ViewHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             new PolicyAdapter$ViewHolder_ViewBinding(this, itemView); | ||||
|             settings = new ArrowExpandable(new ExpandableViewHolder(expandLayout), arrow); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,246 +0,0 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.database.Cursor; | ||||
| import android.os.Build; | ||||
| import android.text.TextUtils; | ||||
| import android.util.Pair; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.SearchView; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
|  | ||||
| import com.topjohnwu.magisk.App; | ||||
| import com.topjohnwu.magisk.ClassMap; | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.components.BaseActivity; | ||||
| import com.topjohnwu.magisk.components.DownloadModuleService; | ||||
| import com.topjohnwu.magisk.container.Module; | ||||
| import com.topjohnwu.magisk.container.Repo; | ||||
| import com.topjohnwu.magisk.database.RepoDatabaseHelper; | ||||
| import com.topjohnwu.magisk.dialogs.CustomAlertDialog; | ||||
| import com.topjohnwu.magisk.uicomponents.MarkDownWindow; | ||||
| import com.topjohnwu.magisk.utils.Event; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.Collections; | ||||
| import java.util.List; | ||||
| import java.util.Map; | ||||
|  | ||||
| import butterknife.BindView; | ||||
| import java9.util.stream.StreamSupport; | ||||
|  | ||||
| public class ReposAdapter | ||||
|         extends SectionedAdapter<ReposAdapter.SectionHolder, ReposAdapter.RepoHolder> | ||||
|         implements Event.AutoListener, SearchView.OnQueryTextListener { | ||||
|  | ||||
|     private static final int UPDATES = 0; | ||||
|     private static final int INSTALLED = 1; | ||||
|     private static final int OTHERS = 2; | ||||
|  | ||||
|     private Map<String, Module> moduleMap; | ||||
|     private RepoDatabaseHelper repoDB; | ||||
|     private List<Pair<Integer, List<Repo>>> repoPairs; | ||||
|     private List<Repo> fullList; | ||||
|     private SearchView mSearch; | ||||
|  | ||||
|     public ReposAdapter() { | ||||
|         repoDB = App.self.repoDB; | ||||
|         moduleMap = Collections.emptyMap(); | ||||
|         fullList = Collections.emptyList(); | ||||
|         repoPairs = new ArrayList<>(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getSectionCount() { | ||||
|         return repoPairs.size(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getItemCount(int section) { | ||||
|         return repoPairs.get(section).second.size(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public SectionHolder onCreateSectionViewHolder(ViewGroup parent) { | ||||
|         View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.section, parent, false); | ||||
|         return new SectionHolder(v); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public RepoHolder onCreateItemViewHolder(ViewGroup parent, int viewType) { | ||||
|         View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_repo, parent, false); | ||||
|         return new RepoHolder(v); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBindSectionViewHolder(SectionHolder holder, int section) { | ||||
|         switch (repoPairs.get(section).first) { | ||||
|             case UPDATES: | ||||
|                 holder.sectionText.setText(R.string.update_available); | ||||
|                 break; | ||||
|             case INSTALLED: | ||||
|                 holder.sectionText.setText(R.string.installed); | ||||
|                 break; | ||||
|             case OTHERS: | ||||
|                 holder.sectionText.setText(R.string.not_installed); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBindItemViewHolder(RepoHolder holder, int section, int position) { | ||||
|         Repo repo = repoPairs.get(section).second.get(position); | ||||
|         Context context = holder.itemView.getContext(); | ||||
|  | ||||
|         String name = repo.getName(); | ||||
|         String version = repo.getVersion(); | ||||
|         String author = repo.getAuthor(); | ||||
|         String description = repo.getDescription(); | ||||
|         String noInfo = context.getString(R.string.no_info_provided); | ||||
|  | ||||
|         holder.title.setText(TextUtils.isEmpty(name) ? noInfo : name); | ||||
|         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.updateTime.setText(context.getString(R.string.updated_on, repo.getLastUpdateString())); | ||||
|  | ||||
|         holder.infoLayout.setOnClickListener(v -> | ||||
|                 MarkDownWindow.show((BaseActivity) context, null, repo.getDetailUrl())); | ||||
|  | ||||
|         holder.downloadImage.setOnClickListener(v -> { | ||||
|             new CustomAlertDialog((BaseActivity) context) | ||||
|                 .setTitle(context.getString(R.string.repo_install_title, repo.getName())) | ||||
|                 .setMessage(context.getString(R.string.repo_install_msg, repo.getDownloadFilename())) | ||||
|                 .setCancelable(true) | ||||
|                 .setPositiveButton(R.string.install, (d, i) -> | ||||
|                         startDownload((BaseActivity) context, repo, true)) | ||||
|                 .setNeutralButton(R.string.download, (d, i) -> | ||||
|                         startDownload((BaseActivity) context, repo, false)) | ||||
|                 .setNegativeButton(R.string.no_thanks, null) | ||||
|                 .show(); | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private void startDownload(BaseActivity activity, Repo repo, Boolean install) { | ||||
|         activity.runWithExternalRW(() -> { | ||||
|             Intent intent = new Intent(activity, ClassMap.get(DownloadModuleService.class)) | ||||
|                     .putExtra("repo", repo).putExtra("install", install); | ||||
|             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { | ||||
|                 activity.startForegroundService(intent); | ||||
|             } else { | ||||
|                 activity.startService(intent); | ||||
|             } | ||||
|         }); | ||||
|     } | ||||
|  | ||||
|     private void updateLists() { | ||||
|         if (mSearch != null) | ||||
|             onQueryTextChange(mSearch.getQuery().toString()); | ||||
|         else | ||||
|             onQueryTextChange(""); | ||||
|     } | ||||
|  | ||||
|     private static boolean noCaseContain(String a, String b) { | ||||
|         return a.toLowerCase().contains(b.toLowerCase()); | ||||
|     } | ||||
|  | ||||
|     public void setSearchView(SearchView view) { | ||||
|         mSearch = view; | ||||
|         mSearch.setOnQueryTextListener(this); | ||||
|     } | ||||
|  | ||||
|     public void notifyDBChanged(boolean refresh) { | ||||
|         try (Cursor c = repoDB.getRepoCursor()) { | ||||
|             fullList = new ArrayList<>(c.getCount()); | ||||
|             while (c.moveToNext()) | ||||
|                 fullList.add(new Repo(c)); | ||||
|         } | ||||
|         if (refresh) | ||||
|             updateLists(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onEvent(int event) { | ||||
|         moduleMap = Event.getResult(event); | ||||
|         updateLists(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int[] getListeningEvents() { | ||||
|         return new int[] {Event.MODULE_LOAD_DONE}; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onQueryTextSubmit(String query) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onQueryTextChange(String s) { | ||||
|         List<Repo> updates = new ArrayList<>(); | ||||
|         List<Repo> installed = new ArrayList<>(); | ||||
|         List<Repo> others = new ArrayList<>(); | ||||
|  | ||||
|         StreamSupport.stream(fullList) | ||||
|                 .filter(repo -> noCaseContain(repo.getName(), s) | ||||
|                         || noCaseContain(repo.getAuthor(), s) | ||||
|                         || noCaseContain(repo.getDescription(), s)) | ||||
|                 .forEach(repo -> { | ||||
|                     Module module = moduleMap.get(repo.getId()); | ||||
|                     if (module != null) { | ||||
|                         if (repo.getVersionCode() > module.getVersionCode()) { | ||||
|                             // Updates | ||||
|                             updates.add(repo); | ||||
|                         } else { | ||||
|                             installed.add(repo); | ||||
|                         } | ||||
|                     } else { | ||||
|                         others.add(repo); | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
|         repoPairs.clear(); | ||||
|         if (!updates.isEmpty()) | ||||
|             repoPairs.add(new Pair<>(UPDATES, updates)); | ||||
|         if (!installed.isEmpty()) | ||||
|             repoPairs.add(new Pair<>(INSTALLED, installed)); | ||||
|         if (!others.isEmpty()) | ||||
|             repoPairs.add(new Pair<>(OTHERS, others)); | ||||
|  | ||||
|         notifyDataSetChanged(); | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     static class SectionHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         @BindView(R.id.section_text) TextView sectionText; | ||||
|  | ||||
|         SectionHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             new ReposAdapter$SectionHolder_ViewBinding(this, itemView); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static class RepoHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         @BindView(R.id.title) TextView title; | ||||
|         @BindView(R.id.version_name) TextView versionName; | ||||
|         @BindView(R.id.description) TextView description; | ||||
|         @BindView(R.id.author) TextView author; | ||||
|         @BindView(R.id.info_layout) View infoLayout; | ||||
|         @BindView(R.id.download) ImageView downloadImage; | ||||
|         @BindView(R.id.update_time) TextView updateTime; | ||||
|  | ||||
|         RepoHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             new ReposAdapter$RepoHolder_ViewBinding(this, itemView); | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
| @@ -1,96 +0,0 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| import android.view.ViewGroup; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
|  | ||||
| public abstract class SectionedAdapter<S extends RecyclerView.ViewHolder, C extends RecyclerView.ViewHolder> | ||||
|         extends RecyclerView.Adapter<RecyclerView.ViewHolder> { | ||||
|  | ||||
|     private static final int SECTION_TYPE = Integer.MIN_VALUE; | ||||
|  | ||||
|     @NonNull | ||||
|     @Override | ||||
|     final public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||
|         if (viewType == SECTION_TYPE) | ||||
|             return onCreateSectionViewHolder(parent); | ||||
|         return onCreateItemViewHolder(parent, viewType); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     @SuppressWarnings("unchecked") | ||||
|     final public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { | ||||
|         PositionInfo info = getPositionInfo(position); | ||||
|         if (info.position == -1) | ||||
|             onBindSectionViewHolder((S) holder, info.section); | ||||
|         else | ||||
|             onBindItemViewHolder((C) holder, info.section, info.position); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     final public int getItemCount() { | ||||
|         int size, sec; | ||||
|         size = sec = getSectionCount(); | ||||
|         for (int i = 0; i < sec; ++i){ | ||||
|             size += getItemCount(i); | ||||
|         } | ||||
|         return size; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     final public int getItemViewType(int position) { | ||||
|         PositionInfo info = getPositionInfo(position); | ||||
|         if (info.position == -1) | ||||
|             return SECTION_TYPE; | ||||
|         else | ||||
|             return getItemViewType(info.section, info.position); | ||||
|     } | ||||
|  | ||||
|     public int getItemViewType(int section, int position) { | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     protected int getSectionPosition(int section) { | ||||
|         return getItemPosition(section, -1); | ||||
|     } | ||||
|  | ||||
|     protected int getItemPosition(int section, int position) { | ||||
|         int realPosition = 0; | ||||
|         // Previous sections | ||||
|         for (int i = 0; i < section; ++i) { | ||||
|             realPosition += getItemCount(i) + 1; | ||||
|         } | ||||
|         // Current section | ||||
|         realPosition += position + 1; | ||||
|         return realPosition; | ||||
|     } | ||||
|  | ||||
|     private PositionInfo getPositionInfo(int position) { | ||||
|         int section = 0; | ||||
|         while (true) { | ||||
|             if (position == 0) | ||||
|                 return new PositionInfo(section, -1); | ||||
|             position -= 1; | ||||
|             if (position < getItemCount(section)) | ||||
|                 return new PositionInfo(section, position); | ||||
|             position -= getItemCount(section++); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private static class PositionInfo { | ||||
|         int section; | ||||
|         int position; | ||||
|         PositionInfo(int section, int position) { | ||||
|             this.section = section; | ||||
|             this.position = position; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public abstract int getSectionCount(); | ||||
|     public abstract int getItemCount(int section); | ||||
|     public abstract S onCreateSectionViewHolder(ViewGroup parent); | ||||
|     public abstract C onCreateItemViewHolder(ViewGroup parent, int viewType); | ||||
|     public abstract void onBindSectionViewHolder(S holder, int section); | ||||
|     public abstract void onBindItemViewHolder(C holder, int section, int position); | ||||
| } | ||||
| @@ -1,103 +0,0 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.util.DisplayMetrics; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import androidx.annotation.IdRes; | ||||
| import androidx.annotation.LayoutRes; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
|  | ||||
| import java.util.List; | ||||
|  | ||||
| public abstract class StringListAdapter<VH extends StringListAdapter.ViewHolder> | ||||
|         extends RecyclerView.Adapter<VH> { | ||||
|  | ||||
|     private RecyclerView rv; | ||||
|     private boolean dynamic; | ||||
|     private int screenWidth; | ||||
|     private int txtWidth = -1; | ||||
|     private int padding; | ||||
|  | ||||
|     protected List<String> mList; | ||||
|  | ||||
|     public StringListAdapter(List<String> list) { | ||||
|         this(list, false); | ||||
|     } | ||||
|  | ||||
|     public StringListAdapter(List<String> list, boolean isDynamic) { | ||||
|         mList = list; | ||||
|         dynamic = isDynamic; | ||||
|     } | ||||
|  | ||||
|     @NonNull | ||||
|     @Override | ||||
|     public final VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { | ||||
|         View v = LayoutInflater.from(parent.getContext()).inflate(itemLayoutRes(), parent, false); | ||||
|         VH vh = createViewHolder(v); | ||||
|         if (txtWidth < 0) | ||||
|             onUpdateTextWidth(vh); | ||||
|         return vh; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBindViewHolder(@NonNull VH holder, int position) { | ||||
|         holder.txt.setText(mList.get(position)); | ||||
|         holder.txt.getLayoutParams().width = txtWidth; | ||||
|         if (dynamic) | ||||
|             onUpdateTextWidth(holder); | ||||
|     } | ||||
|  | ||||
|     protected void onUpdateTextWidth(VH vh) { | ||||
|         if (txtWidth < 0) { | ||||
|             txtWidth = screenWidth - padding; | ||||
|         } else { | ||||
|             vh.txt.measure(0, 0); | ||||
|             int width = vh.txt.getMeasuredWidth(); | ||||
|             if (width > txtWidth) { | ||||
|                 txtWidth = width; | ||||
|                 vh.txt.getLayoutParams().width = txtWidth; | ||||
|             } | ||||
|         } | ||||
|         if (rv.getWidth() != txtWidth + padding) | ||||
|             rv.requestLayout(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onAttachedToRecyclerView(@NonNull RecyclerView rv) { | ||||
|         DisplayMetrics displayMetrics = new DisplayMetrics(); | ||||
|         ((Activity) rv.getContext()).getWindowManager() | ||||
|                 .getDefaultDisplay().getMetrics(displayMetrics); | ||||
|         screenWidth = displayMetrics.widthPixels; | ||||
|         padding = rv.getPaddingStart() + rv.getPaddingEnd(); | ||||
|         this.rv = rv; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public final int getItemCount() { | ||||
|         return mList.size(); | ||||
|     } | ||||
|  | ||||
|     @LayoutRes | ||||
|     protected abstract int itemLayoutRes(); | ||||
|  | ||||
|     @NonNull | ||||
|     public abstract VH createViewHolder(@NonNull View v); | ||||
|  | ||||
|     public static abstract class ViewHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         public TextView txt; | ||||
|  | ||||
|         public ViewHolder(@NonNull View itemView) { | ||||
|             super(itemView); | ||||
|             txt = itemView.findViewById(textViewResId()); | ||||
|         } | ||||
|  | ||||
|         @IdRes | ||||
|         protected abstract int textViewResId(); | ||||
|     } | ||||
| } | ||||
| @@ -1,144 +0,0 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.view.animation.Animation; | ||||
| import android.view.animation.RotateAnimation; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.TextView; | ||||
|  | ||||
| import androidx.recyclerview.widget.RecyclerView; | ||||
|  | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.container.SuLogEntry; | ||||
| import com.topjohnwu.magisk.database.MagiskDB; | ||||
| import com.topjohnwu.magisk.uicomponents.ExpandableViewHolder; | ||||
|  | ||||
| import java.util.Collections; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
|  | ||||
| import butterknife.BindView; | ||||
|  | ||||
| public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, SuLogAdapter.LogViewHolder> { | ||||
|  | ||||
|     private List<List<SuLogEntry>> logEntries; | ||||
|     private Set<Integer> itemExpanded, sectionExpanded; | ||||
|     private MagiskDB suDB; | ||||
|  | ||||
|     public SuLogAdapter(MagiskDB db) { | ||||
|         suDB = db; | ||||
|         logEntries = Collections.emptyList(); | ||||
|         sectionExpanded = new HashSet<>(); | ||||
|         itemExpanded = new HashSet<>(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getSectionCount() { | ||||
|         return logEntries.size(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getItemCount(int section) { | ||||
|         return sectionExpanded.contains(section) ? logEntries.get(section).size() : 0; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public SectionHolder onCreateSectionViewHolder(ViewGroup parent) { | ||||
|         View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog_group, parent, false); | ||||
|         return new SectionHolder(v); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public LogViewHolder onCreateItemViewHolder(ViewGroup parent, int viewType) { | ||||
|         View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog, parent, false); | ||||
|         return new LogViewHolder(v); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBindSectionViewHolder(SectionHolder holder, int section) { | ||||
|         SuLogEntry entry = logEntries.get(section).get(0); | ||||
|         holder.arrow.setRotation(sectionExpanded.contains(section) ? 180 : 0); | ||||
|         holder.itemView.setOnClickListener(v -> { | ||||
|             RotateAnimation rotate; | ||||
|             if (sectionExpanded.contains(section)) { | ||||
|                 holder.arrow.setRotation(0); | ||||
|                 rotate = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); | ||||
|                 sectionExpanded.remove(section); | ||||
|                 notifyItemRangeRemoved(getItemPosition(section, 0), logEntries.get(section).size()); | ||||
|             } else { | ||||
|                 rotate = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); | ||||
|                 sectionExpanded.add(section); | ||||
|                 notifyItemRangeInserted(getItemPosition(section, 0), logEntries.get(section).size()); | ||||
|             } | ||||
|             rotate.setDuration(300); | ||||
|             rotate.setFillAfter(true); | ||||
|             holder.arrow.setAnimation(rotate); | ||||
|         }); | ||||
|         holder.date.setText(entry.getDateString()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onBindItemViewHolder(LogViewHolder holder, int section, int position) { | ||||
|         SuLogEntry entry = logEntries.get(section).get(position); | ||||
|         int realIdx = getItemPosition(section, position); | ||||
|         holder.expandable.setExpanded(itemExpanded.contains(realIdx)); | ||||
|         holder.itemView.setOnClickListener(view -> { | ||||
|             if (holder.expandable.isExpanded()) { | ||||
|                 holder.expandable.collapse(); | ||||
|                 itemExpanded.remove(realIdx); | ||||
|             } else { | ||||
|                 holder.expandable.expand(); | ||||
|                 itemExpanded.add(realIdx); | ||||
|             } | ||||
|         }); | ||||
|         Context context = holder.itemView.getContext(); | ||||
|         holder.appName.setText(entry.appName); | ||||
|         holder.action.setText(entry.action ? R.string.grant : R.string.deny); | ||||
|         holder.pid.setText(context.getString(R.string.pid, entry.fromPid)); | ||||
|         holder.uid.setText(context.getString(R.string.target_uid, entry.toUid)); | ||||
|         holder.command.setText(context.getString(R.string.command, entry.command)); | ||||
|         holder.time.setText(entry.getTimeString()); | ||||
|     } | ||||
|  | ||||
|     public void notifyDBChanged() { | ||||
|         logEntries = suDB.getLogs(); | ||||
|         itemExpanded.clear(); | ||||
|         sectionExpanded.clear(); | ||||
|         sectionExpanded.add(0); | ||||
|         notifyDataSetChanged(); | ||||
|     } | ||||
|  | ||||
|     static class SectionHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         @BindView(R.id.date) TextView date; | ||||
|         @BindView(R.id.arrow) ImageView arrow; | ||||
|  | ||||
|         SectionHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             new SuLogAdapter$SectionHolder_ViewBinding(this, itemView); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     static class LogViewHolder extends RecyclerView.ViewHolder { | ||||
|  | ||||
|         @BindView(R.id.app_name) TextView appName; | ||||
|         @BindView(R.id.action) TextView action; | ||||
|         @BindView(R.id.time) TextView time; | ||||
|         @BindView(R.id.pid) TextView pid; | ||||
|         @BindView(R.id.uid) TextView uid; | ||||
|         @BindView(R.id.cmd) TextView command; | ||||
|         @BindView(R.id.expand_layout) ViewGroup expandLayout; | ||||
|  | ||||
|         ExpandableViewHolder expandable; | ||||
|  | ||||
|         LogViewHolder(View itemView) { | ||||
|             super(itemView); | ||||
|             new SuLogAdapter$LogViewHolder_ViewBinding(this, itemView); | ||||
|             expandable = new ExpandableViewHolder(expandLayout); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,41 +0,0 @@ | ||||
| package com.topjohnwu.magisk.adapters; | ||||
|  | ||||
|  | ||||
| import androidx.fragment.app.Fragment; | ||||
| import androidx.fragment.app.FragmentManager; | ||||
| import androidx.fragment.app.FragmentPagerAdapter; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
|  | ||||
| public class TabFragmentAdapter extends FragmentPagerAdapter { | ||||
|  | ||||
|     private List<Fragment> fragmentList; | ||||
|     private List<String> titleList; | ||||
|  | ||||
|     public TabFragmentAdapter(FragmentManager fm) { | ||||
|         super(fm); | ||||
|         fragmentList = new ArrayList<>(); | ||||
|         titleList = new ArrayList<>(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public Fragment getItem(int position) { | ||||
|         return fragmentList.get(position); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int getCount() { | ||||
|         return fragmentList.size(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public CharSequence getPageTitle(int position) { | ||||
|         return titleList.get(position); | ||||
|     } | ||||
|  | ||||
|     public void addTab(Fragment fragment, String title) { | ||||
|         fragmentList.add(fragment); | ||||
|         titleList.add(title); | ||||
|     } | ||||
| } | ||||
| @@ -1,157 +0,0 @@ | ||||
| package com.topjohnwu.magisk.components; | ||||
|  | ||||
| import android.Manifest; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.content.SharedPreferences; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.os.Bundle; | ||||
| import android.text.TextUtils; | ||||
| import android.view.WindowManager; | ||||
| import android.widget.Toast; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.annotation.StyleRes; | ||||
| import androidx.appcompat.app.AppCompatActivity; | ||||
| import androidx.appcompat.app.AppCompatDelegate; | ||||
| import androidx.core.app.ActivityCompat; | ||||
| import androidx.core.content.ContextCompat; | ||||
|  | ||||
| import com.topjohnwu.magisk.App; | ||||
| import com.topjohnwu.magisk.Config; | ||||
| import com.topjohnwu.magisk.Const; | ||||
| import com.topjohnwu.magisk.R; | ||||
| import com.topjohnwu.magisk.utils.Event; | ||||
| import com.topjohnwu.magisk.utils.LocaleManager; | ||||
|  | ||||
| public abstract class BaseActivity extends AppCompatActivity implements Event.AutoListener { | ||||
|  | ||||
|     public static final String INTENT_PERM = "perm_dialog"; | ||||
|     private static Runnable grantCallback; | ||||
|  | ||||
|     static int[] EMPTY_INT_ARRAY = new int[0]; | ||||
|  | ||||
|     private ActivityResultListener activityResultListener; | ||||
|     public App app = App.self; | ||||
|  | ||||
|     static { | ||||
|         AppCompatDelegate.setCompatVectorFromResourcesEnabled(true); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int[] getListeningEvents() { | ||||
|         return EMPTY_INT_ARRAY; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onEvent(int event) {} | ||||
|  | ||||
|     @StyleRes | ||||
|     public int getDarkTheme() { | ||||
|         return -1; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void attachBaseContext(Context base) { | ||||
|         super.attachBaseContext(LocaleManager.getLocaleContext(base, LocaleManager.locale)); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onCreate(@Nullable Bundle savedInstanceState) { | ||||
|         Event.register(this); | ||||
|         if (getDarkTheme() != -1 && (boolean) Config.get(Config.Key.DARK_THEME)) { | ||||
|             setTheme(getDarkTheme()); | ||||
|         } | ||||
|         super.onCreate(savedInstanceState); | ||||
|         String[] perms = getIntent().getStringArrayExtra(INTENT_PERM); | ||||
|         if (perms != null) | ||||
|             ActivityCompat.requestPermissions(this, perms, 0); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onDestroy() { | ||||
|         Event.unregister(this); | ||||
|         super.onDestroy(); | ||||
|     } | ||||
|  | ||||
|     protected void setFloating() { | ||||
|         boolean isTablet = getResources().getBoolean(R.bool.isTablet); | ||||
|         if (isTablet) { | ||||
|             WindowManager.LayoutParams params = getWindow().getAttributes(); | ||||
|             params.height = getResources().getDimensionPixelSize(R.dimen.floating_height); | ||||
|             params.width = getResources().getDimensionPixelSize(R.dimen.floating_width); | ||||
|             params.alpha = 1.0f; | ||||
|             params.dimAmount = 0.6f; | ||||
|             params.flags |= 2; | ||||
|             getWindow().setAttributes(params); | ||||
|             setFinishOnTouchOutside(true); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     public void runWithExternalRW(Runnable callback) { | ||||
|         runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, callback); | ||||
|     } | ||||
|  | ||||
|     public void runWithPermission(String[] permissions, Runnable callback) { | ||||
|         runWithPermission(this, permissions, callback); | ||||
|     } | ||||
|  | ||||
|     public static void runWithPermission(Context context, String[] permissions, Runnable callback) { | ||||
|         boolean granted = true; | ||||
|         for (String perm : permissions) { | ||||
|             if (ContextCompat.checkSelfPermission(context, perm) != PackageManager.PERMISSION_GRANTED) | ||||
|                 granted = false; | ||||
|         } | ||||
|         if (granted) { | ||||
|             Const.EXTERNAL_PATH.mkdirs(); | ||||
|             callback.run(); | ||||
|         } else { | ||||
|             // Passed in context should be an activity if not granted, need to show dialog! | ||||
|             if (context instanceof BaseActivity) { | ||||
|                 grantCallback = callback; | ||||
|                 ActivityCompat.requestPermissions((BaseActivity) context, permissions, 0); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void onActivityResult(int requestCode, int resultCode, Intent data) { | ||||
|         if (activityResultListener != null) | ||||
|             activityResultListener.onActivityResult(requestCode, resultCode, data); | ||||
|         activityResultListener = null; | ||||
|     } | ||||
|  | ||||
|     public void startActivityForResult(Intent intent, int requestCode, ActivityResultListener listener) { | ||||
|         activityResultListener = listener; | ||||
|         super.startActivityForResult(intent, requestCode); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { | ||||
|         boolean grant = true; | ||||
|         for (int result : grantResults) { | ||||
|             if (result != PackageManager.PERMISSION_GRANTED) | ||||
|                 grant = false; | ||||
|         } | ||||
|         if (grant) { | ||||
|             if (grantCallback != null) { | ||||
|                 grantCallback.run(); | ||||
|             } | ||||
|         } else { | ||||
|             Toast.makeText(this, R.string.no_rw_storage, Toast.LENGTH_LONG).show(); | ||||
|         } | ||||
|         grantCallback = null; | ||||
|     } | ||||
|  | ||||
|     public interface ActivityResultListener { | ||||
|         void onActivityResult(int requestCode, int resultCode, Intent data); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public SharedPreferences getSharedPreferences(String name, int mode) { | ||||
|         if (TextUtils.equals(name, getPackageName() + "_preferences")) | ||||
|             return app.prefs; | ||||
|         return super.getSharedPreferences(name, mode); | ||||
|     } | ||||
| } | ||||
| @@ -1,56 +0,0 @@ | ||||
| package com.topjohnwu.magisk.components; | ||||
|  | ||||
| import android.content.Intent; | ||||
|  | ||||
| import androidx.fragment.app.Fragment; | ||||
|  | ||||
| import com.topjohnwu.magisk.App; | ||||
| import com.topjohnwu.magisk.utils.Event; | ||||
|  | ||||
| import butterknife.Unbinder; | ||||
|  | ||||
| public abstract class BaseFragment extends Fragment implements Event.AutoListener { | ||||
|  | ||||
|     public App app = App.self; | ||||
|     protected Unbinder unbinder = null; | ||||
|  | ||||
|     @Override | ||||
|     public void onResume() { | ||||
|         super.onResume(); | ||||
|         Event.register(this); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onPause() { | ||||
|         Event.unregister(this); | ||||
|         super.onPause(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroyView() { | ||||
|         super.onDestroyView(); | ||||
|         if (unbinder != null) | ||||
|             unbinder.unbind(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void startActivityForResult(Intent intent, int requestCode) { | ||||
|         startActivityForResult(intent, requestCode, this::onActivityResult); | ||||
|     } | ||||
|  | ||||
|     public void startActivityForResult(Intent intent, int requestCode, BaseActivity.ActivityResultListener listener) { | ||||
|         ((BaseActivity) requireActivity()).startActivityForResult(intent, requestCode, listener); | ||||
|     } | ||||
|  | ||||
|     public void runWithPermission(String[] permissions, Runnable callback) { | ||||
|         ((BaseActivity) requireActivity()).runWithPermission(permissions,callback); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int[] getListeningEvents() { | ||||
|         return BaseActivity.EMPTY_INT_ARRAY; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onEvent(int event) {} | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user