mirror of
				https://github.com/topjohnwu/Magisk
				synced 2025-10-31 10:40:52 +01:00 
			
		
		
		
	Compare commits
	
		
			367 Commits
		
	
	
		
			manager-v7
			...
			manager-v7
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 4a697ca2ec | ||
|   | 58bec7f2c9 | ||
|   | 213f84985c | ||
|   | 074b1f8c61 | ||
|   | 326eee8c83 | ||
|   | 00bff4912e | ||
|   | 0ce1720516 | ||
|   | ee407472cf | ||
|   | f341f3b2dd | ||
|   | 8513946e09 | ||
|   | 8ebd9c8927 | ||
|   | 1d54c5144e | ||
|   | e40d4318fa | ||
|   | 7756e10779 | ||
|   | 3e58d502d0 | ||
|   | 1c8846dc57 | ||
|   | 2f320c7239 | ||
|   | e799918ab6 | ||
|   | 86c4928e0f | ||
|   | 0293eb5c51 | ||
|   | 1ee75b6aa6 | ||
|   | 4b30b224b5 | ||
|   | 16b232d2a3 | ||
|   | 3f3b1f5b1d | ||
|   | cec017b7bf | ||
|   | 3123cc1059 | ||
|   | caa9df86bc | ||
|   | f417389a7a | ||
|   | 662a5c8ea6 | ||
|   | 7edfbfb764 | ||
|   | c1602d2554 | ||
|   | 9f8d4e1022 | ||
|   | d1dfda405f | ||
|   | 28efded624 | ||
|   | 06c86ee267 | ||
|   | 5892780871 | ||
|   | 4fcdcd9a8a | ||
|   | 80d834fb55 | ||
|   | 4122ebe18f | ||
|   | 7d87777bf8 | ||
|   | 4a73d634e0 | ||
|   | 373dc10a40 | ||
|   | ed43ec8ea2 | ||
|   | 7918fc3528 | ||
|   | bf58205b0a | ||
|   | c0d1ce96d1 | ||
|   | b31d3802eb | ||
|   | be1228c3b4 | ||
|   | 15c94c6b34 | ||
|   | 202d23426a | ||
|   | fc26de48b2 | ||
|   | 76c88913f9 | ||
|   | a3a1aed723 | ||
|   | 81aa56f60f | ||
|   | 73bb850209 | ||
|   | 8dfec12330 | ||
|   | ae24397793 | ||
|   | 3b0f888407 | ||
|   | 845d1e02b0 | ||
|   | 5d357bc41f | ||
|   | 6a54672b13 | ||
|   | 3d9a15df44 | ||
|   | 449c7fda2f | ||
|   | 8b7b05da68 | ||
|   | 92400ebcab | ||
|   | 23d3e56967 | ||
|   | 6785dc4967 | ||
|   | dad20f6a2d | ||
|   | bb15671046 | ||
|   | 21984fac8b | ||
|   | f392afe87f | ||
|   | 6a243ec7bc | ||
|   | 8cd3b603df | ||
|   | 6e1aefe6d8 | ||
|   | 1c90b6eca3 | ||
|   | c33cf9f878 | ||
|   | 27cb40eec9 | ||
|   | c06081b75d | ||
|   | a7eec2f0a0 | ||
|   | 4fd0fe3194 | ||
|   | cc74593ddd | ||
|   | fdb7c5dba1 | ||
|   | 77470c7cfa | ||
|   | f0a734fdab | ||
|   | 75405b2b25 | ||
|   | 90ed4b3c49 | ||
|   | 290a17a764 | ||
|   | aaabd836e4 | ||
|   | 076e5cea3b | ||
|   | 8515971ccf | ||
|   | d86fb033ea | ||
|   | 99d7d8ddbc | ||
|   | df78fd2d41 | ||
|   | dabe6267b9 | ||
|   | 0119ebddbe | ||
|   | 3216ef9f47 | ||
|   | b79d1bcded | ||
|   | 17e234f9d5 | ||
|   | ea1f75f80e | ||
|   | 8c40db5730 | ||
|   | 6fe03d2795 | ||
|   | c595a87ccf | ||
|   | fac07c3913 | ||
|   | c63fdbbc6b | ||
|   | 2ff5d9606b | ||
|   | ed43452c1a | ||
|   | 8f28d4028f | ||
|   | b54543b18c | ||
|   | 966d6593ca | ||
|   | ad95b1c9d1 | ||
|   | 3bfa38c60a | ||
|   | 0bdbcad8be | ||
|   | 80855e89ec | ||
|   | 0850401dc4 | ||
|   | 337fda2023 | ||
|   | 64f238191e | ||
|   | eb169cb133 | ||
|   | 80cd85b061 | ||
|   | 89275270f3 | ||
|   | e7339ba619 | ||
|   | d9ad7d522c | ||
|   | 92789c3113 | ||
|   | c1c677e161 | ||
|   | 2fe917ff82 | ||
|   | 0e6c205732 | ||
|   | 125ae0a173 | ||
|   | 0245e13591 | ||
|   | d546733287 | ||
|   | c275326d59 | ||
|   | d4561507b8 | ||
|   | ef0e22cc41 | ||
|   | 62db65bf18 | ||
|   | d5371f752c | ||
|   | a5f5e94115 | ||
|   | 2624706c69 | ||
|   | d39d885ec2 | ||
|   | d83c744725 | ||
|   | 843995cdb9 | ||
|   | 9491ba77e0 | ||
|   | 58a449d437 | ||
|   | 7f55e0f05b | ||
|   | 67c3f40adb | ||
|   | ff7a0ba599 | ||
|   | b152c63102 | ||
|   | 415ff23be5 | ||
|   | b0d6de783e | ||
|   | ac28e6e5ca | ||
|   | 4f9e8d2e8a | ||
|   | 21be2f46f3 | ||
|   | a6e7680212 | ||
|   | e79e744e08 | ||
|   | 7abdac72a4 | ||
|   | 90d85eaf7d | ||
|   | e65f9740fb | ||
|   | 7538f89b56 | ||
|   | 7c755a3991 | ||
|   | 10e903c9fc | ||
|   | b018124226 | ||
|   | a9350f50c9 | ||
|   | ed7babcbf1 | ||
|   | 61ebc335c4 | ||
|   | 0167bd76f1 | ||
|   | 79d704008b | ||
|   | 0a703585b0 | ||
|   | 5d632d0d90 | ||
|   | 4eecaea601 | ||
|   | 63055818ec | ||
|   | 0beb08b687 | ||
|   | b27801a27c | ||
|   | a0cfce7cbc | ||
|   | 8b7144c986 | ||
|   | d3f5f5ee59 | ||
|   | a2a3c7f438 | ||
|   | 4496f82d5b | ||
|   | 09d531557d | ||
|   | 7fee82f731 | ||
|   | 475054c48a | ||
|   | a743d05751 | ||
|   | d1ed502e03 | ||
|   | 37744c7ab6 | ||
|   | bbc9e60a12 | ||
|   | 6c975ecc4c | ||
|   | 23e8a4ce4b | ||
|   | 50134a2f9b | ||
|   | 628b37c4fa | ||
|   | 1b4ae70a43 | ||
|   | b25c49725f | ||
|   | b245782c7e | ||
|   | a9f32baae0 | ||
|   | e7ef71865d | ||
|   | 88c4f72b37 | ||
|   | abbcdf91a5 | ||
|   | b876df6e21 | ||
|   | 4bb81f35d7 | ||
|   | ff20267b3f | ||
|   | 2c9586d811 | ||
|   | 2813d2031a | ||
|   | 4040a0242f | ||
|   | 781ec810d9 | ||
|   | 9e90a71c04 | ||
|   | 5571714b26 | ||
|   | e0d1f02ef5 | ||
|   | 1b729e5ff2 | ||
|   | 51e587d4e8 | ||
|   | ac9c55dbc1 | ||
|   | 065051a360 | ||
|   | 0893ac3141 | ||
|   | fb40e96917 | ||
|   | 4ca25f74c6 | ||
|   | 7fda917b86 | ||
|   | e6bd5f2c40 | ||
|   | 8a904ee384 | ||
|   | 00a9f18a1e | ||
|   | 8d68ebb074 | ||
|   | 5f53cfb4a9 | ||
|   | a2fa8d8be1 | ||
|   | 70a3c78ebb | ||
|   | db218407b0 | ||
|   | d52210dd90 | ||
|   | f3cd9a096a | ||
|   | e426090a18 | ||
|   | cbe64fd559 | ||
|   | 63ea7a70bd | ||
|   | fb0998f7a2 | ||
|   | a9b00dd537 | ||
|   | 52eb059515 | ||
|   | 7640246255 | ||
|   | 52c83b2916 | ||
|   | d9cded0fc9 | ||
|   | 750c42caf1 | ||
|   | bbf650c6cf | ||
|   | a25dace7e0 | ||
|   | 14ff22fbcd | ||
|   | 07eb7dda2d | ||
|   | 54d1207f92 | ||
|   | 003e44fb84 | ||
|   | 515f346dcc | ||
|   | d4058175b4 | ||
|   | 2de984ae24 | ||
|   | 761a8bf2a9 | ||
|   | 6df7006b36 | ||
|   | aceb3ee863 | ||
|   | 11d716a3c8 | ||
|   | 7cc8c014eb | ||
|   | f21241d944 | ||
|   | a181fa0652 | ||
|   | 3f748b4d2a | ||
|   | 683450f9c6 | ||
|   | 6050c4e8ba | ||
|   | 158af8819a | ||
|   | 7787bb31fa | ||
|   | a1fe3e7ccd | ||
|   | 4316028b23 | ||
|   | f2b52755d6 | ||
|   | adbd47a36c | ||
|   | ce693aa5e9 | ||
|   | ad80804461 | ||
|   | 2d55632430 | ||
|   | e81f00ef1a | ||
|   | 93fb0e3d74 | ||
|   | 71ce0de606 | ||
|   | 0407062c1d | ||
|   | f315c4416b | ||
|   | cda14af208 | ||
|   | 258f170cd7 | ||
|   | f76015d714 | ||
|   | 7e5e14163c | ||
|   | bcd1064e94 | ||
|   | 8a8441c875 | ||
|   | 15aa813416 | ||
|   | 605faccffd | ||
|   | 79f2d08c81 | ||
|   | 0568ae5391 | ||
|   | 5330dda9f8 | ||
|   | ebab126579 | ||
|   | 0e5417a13e | ||
|   | 9a968e0584 | ||
|   | ffec64d209 | ||
|   | f332746188 | ||
|   | b2fa5b551e | ||
|   | 36e83edddc | ||
|   | 6b045eadef | ||
|   | 147264822c | ||
|   | 36e4ccd800 | ||
|   | 796c16237d | ||
|   | 861ad9881c | ||
|   | 3101c538e9 | ||
|   | 42adc7382f | ||
|   | 9bb4dfad13 | ||
|   | 4e7dafb0e4 | ||
|   | bd00ae8ede | ||
|   | f309522268 | ||
|   | a6395d35db | ||
|   | a028cd5cec | ||
|   | 540000d26e | ||
|   | 888c656aa8 | ||
|   | 0efaddff23 | ||
|   | 94ba7cb0c5 | ||
|   | 2d58c725e0 | ||
|   | e035523eb8 | ||
|   | bea5308ab7 | ||
|   | f006a85fec | ||
|   | ea93013ebc | ||
|   | 8d4c407201 | ||
|   | fdeede23f7 | ||
|   | 53c5ca59b6 | ||
|   | 679db97209 | ||
|   | fbdd72273e | ||
|   | 0165602515 | ||
|   | 96127f8bd1 | ||
|   | 0dbdf336d6 | ||
|   | 48879df2da | ||
|   | b067a5bb13 | ||
|   | 4b54cf1288 | ||
|   | 6128c24f96 | ||
|   | d9c58f307f | ||
|   | b521fbeeda | ||
|   | d00a3b89f2 | ||
|   | 3d15518191 | ||
|   | 9b6535fdf5 | ||
|   | e0424fdba3 | ||
|   | 7481c53451 | ||
|   | 7219947237 | ||
|   | b72004e9cc | ||
|   | f187213568 | ||
|   | fc0df84edd | ||
|   | f24df4f43d | ||
|   | dab32e1599 | ||
|   | bc286fd4d3 | ||
|   | befe1a83b5 | ||
|   | 82ea9db9fd | ||
|   | c5758b3f2d | ||
|   | ace3708c9c | ||
|   | fc5026d268 | ||
|   | 77fd0e54be | ||
|   | 24490e0ff5 | ||
|   | da3937ff4e | ||
|   | ebe1ab982e | ||
|   | 98590cb00d | ||
|   | ff95f634f0 | ||
|   | ced9b4a8ee | ||
|   | 7af7910e78 | ||
|   | a4f5d47e72 | ||
|   | 6953cc2411 | ||
|   | 6a0b2ddee9 | ||
|   | 24f5bc98d8 | ||
|   | 5203886f0b | ||
|   | c10b376575 | ||
|   | ceb21ced2b | ||
|   | 86789a8694 | ||
|   | ca2235aee7 | ||
|   | a385e5cd92 | ||
|   | 0c7a95bdf6 | ||
|   | 036b5acf42 | ||
|   | 056dafc59f | ||
|   | a9c90718d6 | ||
|   | cc77a24502 | ||
|   | 71a91ac7a7 | ||
|   | 08a70f033a | ||
|   | 1b0c36dbd5 | ||
|   | 91da1cf817 | ||
|   | c577a9525d | ||
|   | 0149b1368d | ||
|   | cd6bcb97ef | ||
|   | df4161ffcc | ||
|   | 7a133eaf03 | ||
|   | 1cd45b53b1 | 
							
								
								
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitattributes
									
									
									
									
										vendored
									
									
								
							| @@ -16,3 +16,4 @@ chromeos/** binary | ||||
| *.exe binary | ||||
| *.apk binary | ||||
| *.png binary | ||||
| *.jpg binary | ||||
|   | ||||
							
								
								
									
										21
									
								
								README.MD
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								README.MD
									
									
									
									
									
								
							| @@ -38,6 +38,27 @@ Default string resources for Magisk Manager are scattered throughout | ||||
|  | ||||
| Translate each and place them in the respective locations (`<module>/src/main/res/values-<lang>/strings.xml`). | ||||
|  | ||||
| ## Signature Verification | ||||
|  | ||||
| Official release zips and APKs are signed with my personal private key. You can verify the key certificate to make sure the binaries you downloaded are not manipulated in anyway. | ||||
|  | ||||
| ``` bash | ||||
| # Use the keytool command from JDK to print certificates | ||||
| keytool -printcert -jarfile <APK or Magisk zip> | ||||
|  | ||||
| # The output should contain the following signature | ||||
| Owner: CN=John Wu, L=Taipei, C=TW | ||||
| Issuer: CN=John Wu, L=Taipei, C=TW | ||||
| Serial number: 50514879 | ||||
| Valid from: Sun Aug 14 13:23:44 EDT 2016 until: Tue Jul 21 13:23:44 EDT 2116 | ||||
| Certificate fingerprints: | ||||
| 	 MD5:  CE:DA:68:C1:E1:74:71:0A:EF:58:89:7D:AE:6E:AB:4F | ||||
| 	 SHA1: DC:0F:2B:61:CB:D7:E9:D3:DB:BE:06:0B:2B:87:0D:46:BB:06:02:11 | ||||
| 	 SHA256: B4:CB:83:B4:DA:D9:9F:99:7D:BE:87:2F:01:3A:A1:6C:14:EE:C4:1D:16:70:21:F3:71:F7:E1:33:0F:27:3E:E6 | ||||
| 	 Signature algorithm name: SHA256withRSA | ||||
| 	 Version: 3 | ||||
| ``` | ||||
|  | ||||
| ## License | ||||
|  | ||||
|     Magisk, including all git submodules are free software: | ||||
|   | ||||
							
								
								
									
										119
									
								
								app/build.gradle
									
									
									
									
									
								
							
							
						
						
									
										119
									
								
								app/build.gradle
									
									
									
									
									
								
							| @@ -1,23 +1,56 @@ | ||||
| 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 | ||||
|         javaCompileOptions { | ||||
|             annotationProcessorOptions { | ||||
|                 argument('butterknife.debuggable', 'false') | ||||
|             } | ||||
|         } | ||||
|         multiDexEnabled true | ||||
|         versionName configProps['appVersion'] | ||||
|         versionCode configProps['appVersionCode'] as Integer | ||||
|     } | ||||
|  | ||||
|     buildTypes { | ||||
|         release { | ||||
|             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' | ||||
|             minifyEnabled true | ||||
|             shrinkResources true | ||||
|             proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), | ||||
|                     'proguard-rules.pro', 'proguard-kotlin.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/**' | ||||
|     } | ||||
|  | ||||
|     kotlinOptions { | ||||
|         jvmTarget = '1.8' | ||||
|     } | ||||
| } | ||||
|  | ||||
| androidExtensions { | ||||
|     experimental = true | ||||
| } | ||||
|  | ||||
| dependencies { | ||||
| @@ -25,27 +58,57 @@ 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' | ||||
|     implementation "com.github.topjohnwu.libsu:core:${libsuVersion}" | ||||
|     implementation "com.github.topjohnwu.libsu:io:${libsuVersion}" | ||||
|     def vMarkwon = '3.0.1' | ||||
|     implementation "ru.noties.markwon:core:${vMarkwon}" | ||||
|     implementation "ru.noties.markwon:html:${vMarkwon}" | ||||
|     implementation "ru.noties.markwon:image-svg:${vMarkwon}" | ||||
|  | ||||
|     def butterKnifeVersion = '10.1.0' | ||||
|     implementation "com.jakewharton:butterknife-runtime:${butterKnifeVersion}" | ||||
|     annotationProcessor "com.jakewharton:butterknife-compiler:${butterKnifeVersion}" | ||||
|     def vLibsu = '2.5.0' | ||||
|     implementation "com.github.topjohnwu.libsu:core:${vLibsu}" | ||||
|     implementation "com.github.topjohnwu.libsu:io:${vLibsu}" | ||||
|  | ||||
|     def vKoin = "2.0.1" | ||||
|     implementation "org.koin:koin-core:${vKoin}" | ||||
|     implementation "org.koin:koin-android:${vKoin}" | ||||
|     implementation "org.koin:koin-androidx-viewmodel:${vKoin}" | ||||
|  | ||||
|     def vRetrofit = "2.6.0" | ||||
|     implementation "com.squareup.retrofit2:retrofit:${vRetrofit}" | ||||
|     implementation "com.squareup.retrofit2:converter-moshi:${vRetrofit}" | ||||
|     implementation "com.squareup.retrofit2:adapter-rxjava2:${vRetrofit}" | ||||
|  | ||||
|     def vOkHttp = "3.12.3" | ||||
|     implementation "com.squareup.okhttp3:okhttp:${vOkHttp}" | ||||
|     implementation "com.squareup.okhttp3:logging-interceptor:${vOkHttp}" | ||||
|  | ||||
|     def vMoshi = "1.8.0" | ||||
|     implementation "com.squareup.moshi:moshi:${vMoshi}" | ||||
|  | ||||
|     def vKotshi = "2.0.1" | ||||
|     implementation "se.ansman.kotshi:api:${vKotshi}" | ||||
|     kapt "se.ansman.kotshi:compiler:${vKotshi}" | ||||
|  | ||||
|     modules { | ||||
|         module('androidx.room:room-runtime') { | ||||
|             replacedBy('com.github.topjohnwu:room-runtime') | ||||
|         } | ||||
|     } | ||||
|     def vRoom = "2.1.0" | ||||
|     implementation "com.github.topjohnwu:room-runtime:${vRoom}" | ||||
|  | ||||
|     implementation 'androidx.constraintlayout:constraintlayout:1.1.3' | ||||
|     implementation 'androidx.browser:browser:1.0.0' | ||||
|     implementation 'androidx.preference:preference:1.0.0' | ||||
|     implementation 'androidx.recyclerview:recyclerview:1.1.0-alpha06' | ||||
|     implementation 'androidx.cardview:cardview:1.0.0' | ||||
|     implementation 'androidx.work:work-runtime:2.0.1' | ||||
|     implementation 'androidx.transition:transition:1.2.0-alpha01' | ||||
|     implementation 'androidx.multidex:multidex:2.0.1' | ||||
|     implementation 'com.google.android.material:material:1.1.0-alpha07' | ||||
| } | ||||
|   | ||||
							
								
								
									
										20
									
								
								app/proguard-kotlin.pro
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								app/proguard-kotlin.pro
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| ## So every class is case insensitive to avoid some bizare problems | ||||
| -dontusemixedcaseclassnames | ||||
|  | ||||
| ## If reflection issues come up uncomment this, that should temporarily fix it | ||||
| #-keep class kotlin.** { *; } | ||||
| #-keep class kotlin.Metadata { *; } | ||||
| #-keepclassmembers class kotlin.Metadata { | ||||
| #    public <methods>; | ||||
| #} | ||||
|  | ||||
| ## Never warn about Kotlin, it should work as-is | ||||
| -dontwarn kotlin.** | ||||
|  | ||||
| ## Removes runtime null checks - doesn't really matter if it crashes on kotlin or java NPE | ||||
| -assumenosideeffects class kotlin.jvm.internal.Intrinsics { | ||||
|     static void checkParameterIsNotNull(java.lang.Object, java.lang.String); | ||||
| } | ||||
|  | ||||
| ## Useless option for dex | ||||
| -dontpreverify | ||||
							
								
								
									
										27
									
								
								app/proguard-rules.pro
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										27
									
								
								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,16 +23,19 @@ | ||||
|   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 timber.log.Timber.Tree { *; } | ||||
| -assumenosideeffects class com.topjohnwu.magisk.utils.Logger { | ||||
|   public *** debug(...); | ||||
| } | ||||
| @@ -46,4 +43,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 --> | ||||
|  | ||||
|   | ||||
| @@ -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); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										128
									
								
								app/src/main/java/com/topjohnwu/magisk/App.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								app/src/main/java/com/topjohnwu/magisk/App.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| package com.topjohnwu.magisk | ||||
|  | ||||
| import android.annotation.SuppressLint | ||||
| import android.app.Activity | ||||
| import android.app.Application | ||||
| import android.content.Context | ||||
| 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 androidx.room.Room | ||||
| import androidx.work.impl.WorkDatabase | ||||
| import androidx.work.impl.WorkDatabase_Impl | ||||
| 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.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 | ||||
|  | ||||
|     @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 | ||||
|             Room.setFactory { | ||||
|                 when (it) { | ||||
|                     WorkDatabase::class.java -> WorkDatabase_Impl() | ||||
|                     else -> null | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         @Deprecated("") | ||||
|         @JvmStatic | ||||
|         fun foreground(): Activity? { | ||||
|             val app: App by inject() | ||||
|             return app.foreground | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -1,27 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import com.topjohnwu.magisk.components.DownloadModuleService; | ||||
| import com.topjohnwu.magisk.components.GeneralReceiver; | ||||
| import com.topjohnwu.magisk.components.UpdateCheckService; | ||||
|  | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
|  | ||||
| public class ClassMap { | ||||
|     private static Map<Class, Class> classMap = new HashMap<>(); | ||||
|  | ||||
|     static { | ||||
|         classMap.put(App.class, a.e.class); | ||||
|         classMap.put(MainActivity.class, a.b.class); | ||||
|         classMap.put(SplashActivity.class, a.c.class); | ||||
|         classMap.put(FlashActivity.class, a.f.class); | ||||
|         classMap.put(UpdateCheckService.class, a.g.class); | ||||
|         classMap.put(GeneralReceiver.class, a.h.class); | ||||
|         classMap.put(DownloadModuleService.class, a.j.class); | ||||
|         classMap.put(SuRequestActivity.class, a.m.class); | ||||
|     } | ||||
|      | ||||
|     public static <T> Class<T> get(Class c) { | ||||
|         return classMap.get(c); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										27
									
								
								app/src/main/java/com/topjohnwu/magisk/ClassMap.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								app/src/main/java/com/topjohnwu/magisk/ClassMap.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| package com.topjohnwu.magisk | ||||
|  | ||||
| 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 | ||||
|  | ||||
| object ClassMap { | ||||
|     private val map = mapOf( | ||||
|         App::class.java to a.e::class.java, | ||||
|         MainActivity::class.java to a.b::class.java, | ||||
|         SplashActivity::class.java to a.c::class.java, | ||||
|         FlashActivity::class.java to a.f::class.java, | ||||
|         UpdateCheckService::class.java to a.g::class.java, | ||||
|         GeneralReceiver::class.java to a.h::class.java, | ||||
|         DownloadModuleService::class.java to a.j::class.java, | ||||
|         SuRequestActivity::class.java to a.m::class.java | ||||
|     ) | ||||
|  | ||||
|     @JvmStatic | ||||
|     operator fun <T : Class<*>>get(c: Class<*>): T { | ||||
|         return map.getOrElse(c) { throw IllegalArgumentException() } as T | ||||
|     } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										196
									
								
								app/src/main/java/com/topjohnwu/magisk/Config.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								app/src/main/java/com/topjohnwu/magisk/Config.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,196 @@ | ||||
| package com.topjohnwu.magisk | ||||
|  | ||||
| import android.content.Context | ||||
| import android.util.Xml | ||||
| import androidx.core.content.edit | ||||
| import com.topjohnwu.magisk.data.database.SettingsDao | ||||
| import com.topjohnwu.magisk.data.database.StringDao | ||||
| import com.topjohnwu.magisk.data.repository.DBConfig | ||||
| import com.topjohnwu.magisk.di.Protected | ||||
| import com.topjohnwu.magisk.model.preference.PreferenceModel | ||||
| import com.topjohnwu.magisk.utils.* | ||||
| import com.topjohnwu.superuser.Shell | ||||
| import com.topjohnwu.superuser.io.SuFile | ||||
| import com.topjohnwu.superuser.io.SuFileInputStream | ||||
| import org.xmlpull.v1.XmlPullParser | ||||
| import java.io.File | ||||
|  | ||||
| object Config : PreferenceModel, DBConfig { | ||||
|  | ||||
|     override val stringDao: StringDao by inject() | ||||
|     override val settingsDao: SettingsDao by inject() | ||||
|     override val context: Context by inject(Protected) | ||||
|  | ||||
|     object Key { | ||||
|         // db configs | ||||
|         const val ROOT_ACCESS = "root_access" | ||||
|         const val SU_MULTIUSER_MODE = "multiuser_mode" | ||||
|         const val SU_MNT_NS = "mnt_ns" | ||||
|         const val SU_MANAGER = "requester" | ||||
|         const val SU_FINGERPRINT = "su_fingerprint" | ||||
|  | ||||
|         // prefs | ||||
|         const val SU_REQUEST_TIMEOUT = "su_request_timeout" | ||||
|         const val SU_AUTO_RESPONSE = "su_auto_response" | ||||
|         const val SU_NOTIFICATION = "su_notification" | ||||
|         const val SU_REAUTH = "su_reauth" | ||||
|         const val CHECK_UPDATES = "check_update" | ||||
|         const val UPDATE_CHANNEL = "update_channel" | ||||
|         const val CUSTOM_CHANNEL = "custom_channel" | ||||
|         const val LOCALE = "locale" | ||||
|         const val DARK_THEME = "dark_theme" | ||||
|         const val ETAG_KEY = "ETag" | ||||
|         const val REPO_ORDER = "repo_order" | ||||
|         const val SHOW_SYSTEM_APP = "show_system" | ||||
|  | ||||
|         // system state | ||||
|         const val MAGISKHIDE = "magiskhide" | ||||
|         const val COREONLY = "disable" | ||||
|     } | ||||
|  | ||||
|     object Value { | ||||
|         // Update channels | ||||
|         const val DEFAULT_CHANNEL = -1 | ||||
|         const val STABLE_CHANNEL = 0 | ||||
|         const val BETA_CHANNEL = 1 | ||||
|         const val CUSTOM_CHANNEL = 2 | ||||
|         const val CANARY_CHANNEL = 3 | ||||
|         const val CANARY_DEBUG_CHANNEL = 4 | ||||
|  | ||||
|         // root access mode | ||||
|         const val ROOT_ACCESS_DISABLED = 0 | ||||
|         const val ROOT_ACCESS_APPS_ONLY = 1 | ||||
|         const val ROOT_ACCESS_ADB_ONLY = 2 | ||||
|         const val ROOT_ACCESS_APPS_AND_ADB = 3 | ||||
|  | ||||
|         // su multiuser | ||||
|         const val MULTIUSER_MODE_OWNER_ONLY = 0 | ||||
|         const val MULTIUSER_MODE_OWNER_MANAGED = 1 | ||||
|         const val MULTIUSER_MODE_USER = 2 | ||||
|  | ||||
|         // su mnt ns | ||||
|         const val NAMESPACE_MODE_GLOBAL = 0 | ||||
|         const val NAMESPACE_MODE_REQUESTER = 1 | ||||
|         const val NAMESPACE_MODE_ISOLATE = 2 | ||||
|  | ||||
|         // su notification | ||||
|         const val NO_NOTIFICATION = 0 | ||||
|         const val NOTIFICATION_TOAST = 1 | ||||
|  | ||||
|         // su auto response | ||||
|         const val SU_PROMPT = 0 | ||||
|         const val SU_AUTO_DENY = 1 | ||||
|         const val SU_AUTO_ALLOW = 2 | ||||
|  | ||||
|         // su timeout | ||||
|         val TIMEOUT_LIST = intArrayOf(0, -1, 10, 20, 30, 60) | ||||
|  | ||||
|         // repo order | ||||
|         const val ORDER_NAME = 0 | ||||
|         const val ORDER_DATE = 1 | ||||
|     } | ||||
|  | ||||
|     private val defaultChannel = | ||||
|             if (Utils.isCanary) Value.CANARY_DEBUG_CHANNEL | ||||
|             else Value.DEFAULT_CHANNEL | ||||
|  | ||||
|     var repoOrder by preference(Key.REPO_ORDER, Value.ORDER_DATE) | ||||
|  | ||||
|     var suDefaultTimeout by preferenceStrInt(Key.SU_REQUEST_TIMEOUT, 10) | ||||
|     var suAutoReponse by preferenceStrInt(Key.SU_AUTO_RESPONSE, Value.SU_PROMPT) | ||||
|     var suNotification by preferenceStrInt(Key.SU_NOTIFICATION, Value.NOTIFICATION_TOAST) | ||||
|     var updateChannel by preferenceStrInt(Key.UPDATE_CHANNEL, defaultChannel) | ||||
|  | ||||
|     var darkTheme by preference(Key.DARK_THEME, true) | ||||
|     var suReAuth by preference(Key.SU_REAUTH, false) | ||||
|     var checkUpdate by preference(Key.CHECK_UPDATES, true) | ||||
|     @JvmStatic | ||||
|     var magiskHide by preference(Key.MAGISKHIDE, true) | ||||
|     var coreOnly by preference(Key.COREONLY, false) | ||||
|     var showSystemApp by preference(Key.SHOW_SYSTEM_APP, false) | ||||
|  | ||||
|     var customChannelUrl by preference(Key.CUSTOM_CHANNEL, "") | ||||
|     var locale by preference(Key.LOCALE, "") | ||||
|     @JvmStatic | ||||
|     var etagKey by preference(Key.ETAG_KEY, "") | ||||
|  | ||||
|     var rootMode by dbSettings(Key.ROOT_ACCESS, Value.ROOT_ACCESS_APPS_AND_ADB) | ||||
|     var suMntNamespaceMode by dbSettings(Key.SU_MNT_NS, Value.NAMESPACE_MODE_REQUESTER) | ||||
|     var suMultiuserMode by dbSettings(Key.SU_MULTIUSER_MODE, Value.MULTIUSER_MODE_OWNER_ONLY) | ||||
|     var suFingerprint by dbSettings(Key.SU_FINGERPRINT, false) | ||||
|     @JvmStatic | ||||
|     var suManager by dbStrings(Key.SU_MANAGER, "") | ||||
|  | ||||
|     fun initialize() = prefs.edit { | ||||
|         val config = SuFile.open("/data/adb", Const.MANAGER_CONFIGS) | ||||
|         if (config.exists()) runCatching { | ||||
|             val input = SuFileInputStream(config).buffered() | ||||
|             val parser = Xml.newPullParser() | ||||
|             parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false) | ||||
|             parser.setInput(input, "UTF-8") | ||||
|             parser.nextTag() | ||||
|             parser.require(XmlPullParser.START_TAG, null, "map") | ||||
|             while (parser.next() != XmlPullParser.END_TAG) { | ||||
|                 if (parser.eventType != XmlPullParser.START_TAG) | ||||
|                     continue | ||||
|                 val key: String = parser.getAttributeValue(null, "name") | ||||
|                 val value: String = parser.getAttributeValue(null, "value") | ||||
|                 when (parser.name) { | ||||
|                     "string" -> { | ||||
|                         parser.require(XmlPullParser.START_TAG, null, "string") | ||||
|                         putString(key, parser.nextText()) | ||||
|                         parser.require(XmlPullParser.END_TAG, null, "string") | ||||
|                     } | ||||
|                     "boolean" -> { | ||||
|                         parser.require(XmlPullParser.START_TAG, null, "boolean") | ||||
|                         putBoolean(key, value.toBoolean()) | ||||
|                         parser.nextTag() | ||||
|                         parser.require(XmlPullParser.END_TAG, null, "boolean") | ||||
|                     } | ||||
|                     "int" -> { | ||||
|                         parser.require(XmlPullParser.START_TAG, null, "int") | ||||
|                         putInt(key, value.toInt()) | ||||
|                         parser.nextTag() | ||||
|                         parser.require(XmlPullParser.END_TAG, null, "int") | ||||
|                     } | ||||
|                     "long" -> { | ||||
|                         parser.require(XmlPullParser.START_TAG, null, "long") | ||||
|                         putLong(key, value.toLong()) | ||||
|                         parser.nextTag() | ||||
|                         parser.require(XmlPullParser.END_TAG, null, "long") | ||||
|                     } | ||||
|                     "float" -> { | ||||
|                         parser.require(XmlPullParser.START_TAG, null, "int") | ||||
|                         putFloat(key, value.toFloat()) | ||||
|                         parser.nextTag() | ||||
|                         parser.require(XmlPullParser.END_TAG, null, "int") | ||||
|                     } | ||||
|                     else -> parser.next() | ||||
|                 } | ||||
|             } | ||||
|             config.delete() | ||||
|         } | ||||
|         remove(Key.ETAG_KEY) | ||||
|         if (!prefs.contains(Key.UPDATE_CHANNEL)) | ||||
|             putString(Key.UPDATE_CHANNEL, defaultChannel.toString()) | ||||
|  | ||||
|         // Get actual state | ||||
|         putBoolean(Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists()) | ||||
|  | ||||
|         // Write database configs | ||||
|         putString(Key.ROOT_ACCESS, rootMode.toString()) | ||||
|         putString(Key.SU_MNT_NS, suMntNamespaceMode.toString()) | ||||
|         putString(Key.SU_MULTIUSER_MODE, suMultiuserMode.toString()) | ||||
|         putBoolean(Key.SU_FINGERPRINT, FingerprintHelper.useFingerprint()) | ||||
|     } | ||||
|  | ||||
|     @JvmStatic | ||||
|     fun export() { | ||||
|         // Flush prefs to disk | ||||
|         prefs.edit().apply() | ||||
|         val xml = File("${get<Context>(Protected).filesDir.parent}/shared_prefs", | ||||
|                 "${packageName}_preferences.xml") | ||||
|         Shell.su("cat $xml > /data/adb/${Const.MANAGER_CONFIGS}").exec() | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -1,105 +0,0 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import android.os.Environment; | ||||
| import android.os.Process; | ||||
|  | ||||
| import java.io.File; | ||||
|  | ||||
| public class Const { | ||||
|  | ||||
|     public static final String DEBUG_TAG = "MagiskManager"; | ||||
|  | ||||
|     // APK content | ||||
|     public static final String ANDROID_MANIFEST = "AndroidManifest.xml"; | ||||
|  | ||||
|     public static final String SU_KEYSTORE_KEY = "su_key"; | ||||
|  | ||||
|     // Paths | ||||
|     public static final String MAGISK_PATH = "/sbin/.magisk/img"; | ||||
|     public static final File EXTERNAL_PATH; | ||||
|     public static File MAGISK_DISABLE_FILE; | ||||
|  | ||||
|     static { | ||||
|         MAGISK_DISABLE_FILE = new File("xxx"); | ||||
|         EXTERNAL_PATH = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS); | ||||
|         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; | ||||
|  | ||||
|     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; | ||||
|         public static final int APK_UPDATE_NOTIFICATION_ID = 5; | ||||
|         public static final int DTBO_NOTIFICATION_ID = 7; | ||||
|         public static final int HIDE_MANAGER_NOTIFICATION_ID = 8; | ||||
|         public static final String UPDATE_NOTIFICATION_CHANNEL = "update"; | ||||
|         public static final String PROGRESS_NOTIFICATION_CHANNEL = "progress"; | ||||
|         public static final String CHECK_MAGISK_UPDATE_WORKER_ID = "magisk_update"; | ||||
|     } | ||||
|  | ||||
|     public static class Url { | ||||
|         private static String getRaw(String where, String name) { | ||||
|             return String.format("https://raw.githubusercontent.com/topjohnwu/magisk_files/%s/%s", where, name); | ||||
|         } | ||||
|         public static final String STABLE_URL = getRaw("master", "stable.json"); | ||||
|         public static final String BETA_URL = getRaw("master", "beta.json"); | ||||
|         public static final String CANARY_URL = getRaw("master", "canary_builds/release.json"); | ||||
|         public static final String CANARY_DEBUG_URL = getRaw("master", "canary_builds/canary.json"); | ||||
|         public static final String REPO_URL = "https://api.github.com/users/Magisk-Modules-Repo/repos?per_page=100&sort=pushed&page=%d"; | ||||
|         public static final String FILE_URL = "https://raw.githubusercontent.com/Magisk-Modules-Repo/%s/master/%s"; | ||||
|         public static final String ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip"; | ||||
|         public static final String MODULE_INSTALLER = "https://raw.githubusercontent.com/topjohnwu/Magisk/master/scripts/module_installer.sh"; | ||||
|         public static final String PAYPAL_URL = "https://www.paypal.me/topjohnwu"; | ||||
|         public static final String PATREON_URL = "https://www.patreon.com/topjohnwu"; | ||||
|         public static final String TWITTER_URL = "https://twitter.com/topjohnwu"; | ||||
|         public static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382"; | ||||
|         public static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/Magisk"; | ||||
|         public static final String SNET_URL = getRaw("b66b1a914978e5f4c4bbfd74a59f4ad371bac107", "snet.apk"); | ||||
|         public static final String BOOTCTL_URL = getRaw("9c5dfc1b8245c0b5b524901ef0ff0f8335757b77", "bootctl"); | ||||
|     } | ||||
|  | ||||
|     public static class Key { | ||||
|         // others | ||||
|         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 FLASH_MAGISK = "magisk"; | ||||
|         public static final String FLASH_INACTIVE_SLOT = "slot"; | ||||
|         public static final String UNINSTALL = "uninstall"; | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
							
								
								
									
										98
									
								
								app/src/main/java/com/topjohnwu/magisk/Const.kt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								app/src/main/java/com/topjohnwu/magisk/Const.kt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,98 @@ | ||||
| package com.topjohnwu.magisk | ||||
|  | ||||
| import android.os.Environment | ||||
| import android.os.Process | ||||
|  | ||||
| import java.io.File | ||||
|  | ||||
| object Const { | ||||
|  | ||||
|     const val DEBUG_TAG = "MagiskManager" | ||||
|  | ||||
|     // Paths | ||||
|     const val MAGISK_PATH = "/sbin/.magisk/img" | ||||
|     @JvmField | ||||
|     val EXTERNAL_PATH = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)!! | ||||
|     @JvmField | ||||
|     var MAGISK_DISABLE_FILE = File("xxx") | ||||
|     const val TMP_FOLDER_PATH = "/dev/tmp" | ||||
|     const val MAGISK_LOG = "/cache/magisk.log" | ||||
|  | ||||
|     // Versions | ||||
|     const val SNET_EXT_VER = 12 | ||||
|     const val SNET_REVISION = "b66b1a914978e5f4c4bbfd74a59f4ad371bac107" | ||||
|     const val BOOTCTL_REVISION = "9c5dfc1b8245c0b5b524901ef0ff0f8335757b77" | ||||
|  | ||||
|     // Misc | ||||
|     const val ANDROID_MANIFEST = "AndroidManifest.xml" | ||||
|     const val MAGISK_INSTALL_LOG_FILENAME = "magisk_install_log_%s.log" | ||||
|     const val MANAGER_CONFIGS = ".tmp.magisk.config" | ||||
|     @JvmField | ||||
|     val USER_ID = Process.myUid() / 100000 | ||||
|  | ||||
|     init { | ||||
|         EXTERNAL_PATH.mkdirs() | ||||
|     } | ||||
|  | ||||
|     object MagiskVersion { | ||||
|         const val MIN_SUPPORT = 18000 | ||||
|     } | ||||
|  | ||||
|     object ID { | ||||
|         const val FETCH_ZIP = 2 | ||||
|         const val SELECT_BOOT = 3 | ||||
|  | ||||
|         // notifications | ||||
|         const val MAGISK_UPDATE_NOTIFICATION_ID = 4 | ||||
|         const val APK_UPDATE_NOTIFICATION_ID = 5 | ||||
|         const val DTBO_NOTIFICATION_ID = 7 | ||||
|         const val HIDE_MANAGER_NOTIFICATION_ID = 8 | ||||
|         const val UPDATE_NOTIFICATION_CHANNEL = "update" | ||||
|         const val PROGRESS_NOTIFICATION_CHANNEL = "progress" | ||||
|         const val CHECK_MAGISK_UPDATE_WORKER_ID = "magisk_update" | ||||
|     } | ||||
|  | ||||
|     object Url { | ||||
|         @Deprecated("This shouldn't be used. There's literally no need for it") | ||||
|         const val REPO_URL = | ||||
|             "https://api.github.com/users/Magisk-Modules-Repo/repos?per_page=100&sort=pushed&page=%d" | ||||
|         const val FILE_URL = "https://raw.githubusercontent.com/Magisk-Modules-Repo/%s/master/%s" | ||||
|         const val ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip" | ||||
|         const val MODULE_INSTALLER = | ||||
|             "https://raw.githubusercontent.com/topjohnwu/Magisk/master/scripts/module_installer.sh" | ||||
|         const val PAYPAL_URL = "https://www.paypal.me/topjohnwu" | ||||
|         const val PATREON_URL = "https://www.patreon.com/topjohnwu" | ||||
|         const val TWITTER_URL = "https://twitter.com/topjohnwu" | ||||
|         const val XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382" | ||||
|         const val SOURCE_CODE_URL = "https://github.com/topjohnwu/Magisk" | ||||
|         @JvmField | ||||
|         val BOOTCTL_URL = getRaw("9c5dfc1b8245c0b5b524901ef0ff0f8335757b77", "bootctl") | ||||
|         const val GITHUB_RAW_API_URL = "https://raw.githubusercontent.com/" | ||||
|  | ||||
|         private fun getRaw(where: String, name: String) = | ||||
|             "${GITHUB_RAW_API_URL}topjohnwu/magisk_files/$where/$name" | ||||
|     } | ||||
|  | ||||
|     object Key { | ||||
|         // others | ||||
|         const val LINK_KEY = "Link" | ||||
|         const val IF_NONE_MATCH = "If-None-Match" | ||||
|         // intents | ||||
|         const val OPEN_SECTION = "section" | ||||
|         const val INTENT_SET_NAME = "filename" | ||||
|         const val INTENT_SET_LINK = "link" | ||||
|         const val FLASH_ACTION = "action" | ||||
|         const val BROADCAST_MANAGER_UPDATE = "manager_update" | ||||
|         const val BROADCAST_REBOOT = "reboot" | ||||
|     } | ||||
|  | ||||
|     object Value { | ||||
|         const val FLASH_ZIP = "flash" | ||||
|         const val PATCH_FILE = "patch" | ||||
|         const val FLASH_MAGISK = "magisk" | ||||
|         const val FLASH_INACTIVE_SLOT = "slot" | ||||
|         const val UNINSTALL = "uninstall" | ||||
|     } | ||||
|  | ||||
|  | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										36
									
								
								app/src/main/java/com/topjohnwu/magisk/Info.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								app/src/main/java/com/topjohnwu/magisk/Info.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| package com.topjohnwu.magisk; | ||||
|  | ||||
| import com.topjohnwu.superuser.Shell; | ||||
| import com.topjohnwu.superuser.ShellUtils; | ||||
|  | ||||
| public final class Info { | ||||
|  | ||||
|     public static int magiskVersionCode = -1; | ||||
|     // Current status | ||||
|     public static String magiskVersionString = ""; | ||||
|     // Update Info | ||||
|     public static String remoteMagiskVersionString = ""; | ||||
|     public static int remoteMagiskVersionCode = -1; | ||||
|     public static String magiskLink = ""; | ||||
|     public static String magiskNoteLink = ""; | ||||
|     public static String magiskMD5 = ""; | ||||
|     public static String remoteManagerVersionString = ""; | ||||
|     public static int remoteManagerVersionCode = -1; | ||||
|     public static String managerLink = ""; | ||||
|     public static String managerNoteLink = ""; | ||||
|     public static String uninstallerLink = ""; | ||||
|  | ||||
|     // Install flags | ||||
|     public static boolean keepVerity = false; | ||||
|     public static boolean keepEnc = false; | ||||
|     public static boolean recovery = false; | ||||
|  | ||||
|     public static void loadMagiskInfo() { | ||||
|         try { | ||||
|             magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0]; | ||||
|             magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V")); | ||||
|             Config.setMagiskHide(Shell.su("magiskhide --status").exec().isSuccess()); | ||||
|         } catch (NumberFormatException ignored) { | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -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); | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user