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