mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-11-03 15:52:30 +01:00 
			
		
		
		
	Compare commits
	
		
			229 Commits
		
	
	
		
	
	| 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 | ||
| 
						 | 
					5b30c77403 | ||
| 
						 | 
					8248480d56 | ||
| 
						 | 
					345d992d39 | ||
| 
						 | 
					a7f6afa4bc | ||
| 
						 | 
					d22c7de79a | 
							
								
								
									
										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