mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-08-14 16:17:27 +00:00
Compare commits
251 Commits
v17.1
...
manager-v6
Author | SHA1 | Date | |
---|---|---|---|
![]() |
a54eaf5371 | ||
![]() |
8032bd4bb9 | ||
![]() |
ea1beec2f7 | ||
![]() |
05f2f6820e | ||
![]() |
0f5f15a5ce | ||
![]() |
14ac37e8a5 | ||
![]() |
1fae89cbb6 | ||
![]() |
8b4008798f | ||
![]() |
fd4faf59b8 | ||
![]() |
109891d668 | ||
![]() |
bdea796121 | ||
![]() |
c8813c05c9 | ||
![]() |
1cff08ce5d | ||
![]() |
a868118f6f | ||
![]() |
e5c62f5750 | ||
![]() |
4084e8790b | ||
![]() |
8d931dd773 | ||
![]() |
25d6366297 | ||
![]() |
08cd5b81d1 | ||
![]() |
5d3a8a5b1a | ||
![]() |
79b84da4b8 | ||
![]() |
68b07c5913 | ||
![]() |
553db9124d | ||
![]() |
b495f37299 | ||
![]() |
1915547594 | ||
![]() |
03de29164a | ||
![]() |
86d8b50547 | ||
![]() |
7b04386162 | ||
![]() |
07bfdf3e4d | ||
![]() |
d510224e2a | ||
![]() |
e658f9297d | ||
![]() |
2b502e9a0f | ||
![]() |
59141f9bbe | ||
![]() |
3af66b72f2 | ||
![]() |
422c24bd68 | ||
![]() |
f0f87c8eb9 | ||
![]() |
80dad54119 | ||
![]() |
56a76df28e | ||
![]() |
ee2c801fe0 | ||
![]() |
fc314cc248 | ||
![]() |
fe231a4c80 | ||
![]() |
2e2bbe0a7f | ||
![]() |
857e6e8345 | ||
![]() |
3402981ada | ||
![]() |
f401e577e5 | ||
![]() |
0241a50c6f | ||
![]() |
2a2e1236fc | ||
![]() |
9b170f2b4f | ||
![]() |
51e9ff59de | ||
![]() |
2977dbcded | ||
![]() |
ac60b51035 | ||
![]() |
4c2f33a089 | ||
![]() |
3b071116ac | ||
![]() |
a9f265a591 | ||
![]() |
5b62fc8103 | ||
![]() |
0598f5f89a | ||
![]() |
f723427b8b | ||
![]() |
f69a004c1c | ||
![]() |
1134b18a8b | ||
![]() |
2e4aa507f7 | ||
![]() |
5fb96cdcf4 | ||
![]() |
e8cba3524e | ||
![]() |
7e6b5363f1 | ||
![]() |
29457a1d28 | ||
![]() |
731455f164 | ||
![]() |
b01a8cace6 | ||
![]() |
72db5b4fac | ||
![]() |
ddfd42994e | ||
![]() |
2a9ff9c5ef | ||
![]() |
6d49f05356 | ||
![]() |
85a5e62e36 | ||
![]() |
e67965a381 | ||
![]() |
ec4723096f | ||
![]() |
762b678d24 | ||
![]() |
38fcc57bbf | ||
![]() |
c8c57c74cc | ||
![]() |
0784448c69 | ||
![]() |
de0064af47 | ||
![]() |
baae1fc84f | ||
![]() |
2ab999f4ca | ||
![]() |
c9f390d6e0 | ||
![]() |
af05922ecc | ||
![]() |
299edbf3ab | ||
![]() |
c8abed9d48 | ||
![]() |
3622c49ce1 | ||
![]() |
0462e9a7d9 | ||
![]() |
c3a6091908 | ||
![]() |
ab5fedda0b | ||
![]() |
ba70269398 | ||
![]() |
77fd5fa7de | ||
![]() |
ab74290fe3 | ||
![]() |
3aad9d8166 | ||
![]() |
572e078d87 | ||
![]() |
ee4548230b | ||
![]() |
96b93bd876 | ||
![]() |
927f69fe30 | ||
![]() |
7e9ad5927a | ||
![]() |
6d6b07865e | ||
![]() |
376e7977f0 | ||
![]() |
83ae66daea | ||
![]() |
89e0be0099 | ||
![]() |
ef40c1212e | ||
![]() |
3a2a2a4ffa | ||
![]() |
9592a69986 | ||
![]() |
89be07e1f2 | ||
![]() |
c61c3ae0e9 | ||
![]() |
817350c8c5 | ||
![]() |
3603b7c82b | ||
![]() |
5743c72cca | ||
![]() |
4cdd66ceff | ||
![]() |
d3947d2cfa | ||
![]() |
07718b994a | ||
![]() |
ef9d463bd7 | ||
![]() |
8745c7884e | ||
![]() |
b6965105b7 | ||
![]() |
3d269fe8be | ||
![]() |
be5f00aa1a | ||
![]() |
59ba350f34 | ||
![]() |
803c5377a6 | ||
![]() |
7c12bf7fa1 | ||
![]() |
ca35a9681f | ||
![]() |
9fe5f37337 | ||
![]() |
0742901cd2 | ||
![]() |
5e4d2dedbe | ||
![]() |
411ea56a3e | ||
![]() |
cda57dd4b4 | ||
![]() |
4351de503f | ||
![]() |
6339ba6bfb | ||
![]() |
ef6677f43d | ||
![]() |
a7824af5a8 | ||
![]() |
1eb7d7b7a8 | ||
![]() |
11c33d4447 | ||
![]() |
b8a3cc8b60 | ||
![]() |
27c688252d | ||
![]() |
3e2afd4b1d | ||
![]() |
f45b0686d2 | ||
![]() |
1f3f881f81 | ||
![]() |
ceb51bb14f | ||
![]() |
3e22573d8d | ||
![]() |
79418a3767 | ||
![]() |
40d4683de1 | ||
![]() |
79e5b54ec7 | ||
![]() |
bd81923f2f | ||
![]() |
69560b8ad7 | ||
![]() |
dc413e7b73 | ||
![]() |
7fc00c446b | ||
![]() |
2efc423cf8 | ||
![]() |
8ec3086cdd | ||
![]() |
5fc7079023 | ||
![]() |
bfbd254be7 | ||
![]() |
f8ea43466c | ||
![]() |
75ab1fa570 | ||
![]() |
bf4a46d57c | ||
![]() |
1046dd5eda | ||
![]() |
f9e32a119a | ||
![]() |
dbb8b8a439 | ||
![]() |
2a65c3dc8f | ||
![]() |
f17ec9e9d7 | ||
![]() |
675d6d8328 | ||
![]() |
6dc9ccad75 | ||
![]() |
6add02702b | ||
![]() |
958d6377e3 | ||
![]() |
9954154ca2 | ||
![]() |
4ecbf8c12c | ||
![]() |
fc8a3c5fb4 | ||
![]() |
01e7dff1a0 | ||
![]() |
018c0064cd | ||
![]() |
c2b016370b | ||
![]() |
fc791b4371 | ||
![]() |
f76bb009f4 | ||
![]() |
8a1292b295 | ||
![]() |
d7d80d3fc1 | ||
![]() |
41b01003fd | ||
![]() |
6557070ae1 | ||
![]() |
e7e580e177 | ||
![]() |
dd9ddd2019 | ||
![]() |
74aae523ba | ||
![]() |
48c40f9516 | ||
![]() |
e0e7674715 | ||
![]() |
e1a65276b9 | ||
![]() |
469adc85ad | ||
![]() |
e1b181ca4e | ||
![]() |
a4f0fbf8b7 | ||
![]() |
190cdaddf8 | ||
![]() |
5c4ba13839 | ||
![]() |
e62630cf3e | ||
![]() |
36fe7846c0 | ||
![]() |
8d150dd67a | ||
![]() |
506df00d81 | ||
![]() |
a9121fa28f | ||
![]() |
d5a56d9e85 | ||
![]() |
acf7c0c665 | ||
![]() |
619d48c97a | ||
![]() |
2cb198c38c | ||
![]() |
e8e39e0f3c | ||
![]() |
37860181d4 | ||
![]() |
d119dd9a0c | ||
![]() |
09ef19f7ec | ||
![]() |
6a06c92fa6 | ||
![]() |
58ae596b0f | ||
![]() |
f1ca21678d | ||
![]() |
d7eeef2c8a | ||
![]() |
4f626897f2 | ||
![]() |
b127e01845 | ||
![]() |
2118beeb23 | ||
![]() |
5020cd1bbf | ||
![]() |
cce636224c | ||
![]() |
60b3b8ddce | ||
![]() |
41446ec9ba | ||
![]() |
df8b047bca | ||
![]() |
12ced52012 | ||
![]() |
1d53335ae5 | ||
![]() |
971a50d290 | ||
![]() |
36dd9106a8 | ||
![]() |
0a4ee3ffc7 | ||
![]() |
cfe32f1a70 | ||
![]() |
d877f5d5c6 | ||
![]() |
0ab6ffefb4 | ||
![]() |
a292a1d23a | ||
![]() |
3f87f6aee3 | ||
![]() |
04bcd145d3 | ||
![]() |
244e811291 | ||
![]() |
ac7467fb59 | ||
![]() |
2c0436216f | ||
![]() |
017fbf267b | ||
![]() |
e6afbf2ec0 | ||
![]() |
906b4aad9e | ||
![]() |
4cf8d41f6a | ||
![]() |
47c860142e | ||
![]() |
2fba3f213b | ||
![]() |
af7c6f9fce | ||
![]() |
78534deab6 | ||
![]() |
6710314832 | ||
![]() |
0cd4fa6fa0 | ||
![]() |
065949496e | ||
![]() |
39c82576ae | ||
![]() |
37221a508d | ||
![]() |
6b43a32a10 | ||
![]() |
d7cd1ff142 | ||
![]() |
659d947863 | ||
![]() |
39be7a6288 | ||
![]() |
8ac976c579 | ||
![]() |
70fd432c57 | ||
![]() |
00135f2f49 | ||
![]() |
9b944bc29c | ||
![]() |
d520b3d2a0 | ||
![]() |
6f41d9855b | ||
![]() |
2d7c1da741 | ||
![]() |
c0f45b6b1e | ||
![]() |
7a0025673c | ||
![]() |
ad7ec79903 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -3,9 +3,7 @@ out
|
|||||||
*.jks
|
*.jks
|
||||||
*.apk
|
*.apk
|
||||||
config.prop
|
config.prop
|
||||||
|
update.sh
|
||||||
# Manually dumped jars
|
|
||||||
snet/libs
|
|
||||||
|
|
||||||
# Built binaries
|
# Built binaries
|
||||||
native/out
|
native/out
|
||||||
|
22
README.MD
22
README.MD
@@ -1,7 +1,15 @@
|
|||||||
# Magisk
|
# Magisk
|
||||||
|
[Downloads](https://github.com/topjohnwu/Magisk/releases) | [Documentation](https://topjohnwu.github.io/Magisk/) | [XDA Thread](https://forum.xda-developers.com/apps/magisk/official-magisk-v7-universal-systemless-t3473445)
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
Magisk is a suite of open source tools for customizing Android, supporting devices higher than Android 5.0 (API 21). It covers the fundamental parts for Android customization: root, boot scripts, SELinux patches, AVB2.0 / dm-verity / forceencrypt removals etc.
|
||||||
|
|
||||||
|
Furthermore, Magisk provides a **Systemless Interface** to alter the system (or vendor) arbitrarily while the actual partitions stay completely intact. With its systemless nature along with several other hacks, Magisk can hide modifications from nearly any system integrity verifications used in banking apps, corporation monitoring apps, game cheat detections, and most importantly [Google's SafetyNet API](https://developer.android.com/training/safetynet/index.html).
|
||||||
|
|
||||||
|
## Bug Reports
|
||||||
|
**Make sure to install the latest [Canary Build](https://forum.xda-developers.com/apps/magisk/dev-magisk-canary-channel-bleeding-edge-t3839337) before reporting any bugs!** **DO NOT** report bugs that is already fixed upstream. Follow the instructions in the [Canary Channel XDA Thread](https://forum.xda-developers.com/apps/magisk/dev-magisk-canary-channel-bleeding-edge-t3839337), and report a bug either by opening an issue on GitHub or directly in the thread.
|
||||||
|
|
||||||
## Building Environment Requirements
|
## Building Environment Requirements
|
||||||
|
|
||||||
1. Python 3.5+: run `build.py` script
|
1. Python 3.5+: run `build.py` script
|
||||||
2. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips
|
2. Java Development Kit (JDK) 8: Compile Magisk Manager and sign zips
|
||||||
3. Latest Android SDK: set `ANDROID_HOME` environment variable to the path to Android SDK
|
3. Latest Android SDK: set `ANDROID_HOME` environment variable to the path to Android SDK
|
||||||
@@ -9,13 +17,11 @@
|
|||||||
5. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes
|
5. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes
|
||||||
|
|
||||||
## Building Notes and Instructions
|
## Building Notes and Instructions
|
||||||
1. Building is supported on macOS, Linux, and Windows using the custom NDK: [FrankeNDK](https://github.com/topjohnwu/FrankeNDK).
|
1. Clone sources with submodules: `git clone --recurse-submodules https://github.com/topjohnwu/Magisk.git`
|
||||||
2. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
|
2. Building is supported on macOS, Linux, and Windows. Official releases are built and tested with [FrankeNDK](https://github.com/topjohnwu/FrankeNDK); point `ANDROID_NDK_HOME` to FrankeNDK if you want to use it for compiling.
|
||||||
3. Run `build.py` with argument `-h` to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h`
|
3. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
|
||||||
4. By default, `build.py` build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (via the `-r, --release` flag), you need a Java Keystore file `release-key.jks` (only `JKS` format is supported) to sign APKs and zips. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
|
4. Run `build.py` with argument `-h` to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h`
|
||||||
|
5. By default, `build.py` build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (via the `-r, --release` flag), you need a Java Keystore file `release-key.jks` (only `JKS` format is supported) to sign APKs and zips. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
|
||||||
## Documentation
|
|
||||||
[Link to Documentation](docs/README.MD)
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
@@ -1,7 +1,8 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
def configProps = new Properties()
|
def configProps = new Properties()
|
||||||
configProps.load(new FileInputStream(rootProject.file('config.prop')))
|
def configPath = project.hasProperty('configPath') ? project.configPath : rootProject.file('config.prop')
|
||||||
|
configProps.load(new FileInputStream(configPath))
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
@@ -69,15 +70,23 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
|
implementation 'androidx.core:core:1.0.1'
|
||||||
fullImplementation project(':utils')
|
fullImplementation project(':utils')
|
||||||
implementation "com.android.support:support-core-utils:${rootProject.ext.supportLibVersion}"
|
fullImplementation 'com.amitshekhar.android:android-networking:1.0.2'
|
||||||
fullImplementation "com.android.support:preference-v7:${rootProject.ext.supportLibVersion}"
|
fullImplementation 'androidx.appcompat:appcompat:1.0.2'
|
||||||
fullImplementation "com.android.support:recyclerview-v7:${rootProject.ext.supportLibVersion}"
|
fullImplementation "androidx.preference:preference:${rootProject.ext.androidXVersion}"
|
||||||
fullImplementation "com.android.support:cardview-v7:${rootProject.ext.supportLibVersion}"
|
fullImplementation "androidx.recyclerview:recyclerview:${rootProject.ext.androidXVersion}"
|
||||||
fullImplementation "com.android.support:design:${rootProject.ext.supportLibVersion}"
|
fullImplementation "androidx.cardview:cardview:${rootProject.ext.androidXVersion}"
|
||||||
fullImplementation 'com.github.topjohnwu:libsu:2.0.1'
|
fullImplementation "com.google.android.material:material:${rootProject.ext.androidXVersion}"
|
||||||
|
fullImplementation 'com.github.topjohnwu:libsu:2.1.2'
|
||||||
fullImplementation 'com.atlassian.commonmark:commonmark:0.11.0'
|
fullImplementation 'com.atlassian.commonmark:commonmark:0.11.0'
|
||||||
fullImplementation 'org.kamranzafar:jtar:2.3'
|
fullImplementation 'org.kamranzafar:jtar:2.3'
|
||||||
fullImplementation 'com.jakewharton:butterknife:8.8.1'
|
|
||||||
fullAnnotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
|
def butterKnifeVersion = '9.0.0-rc2'
|
||||||
|
if (properties.containsKey('android.injected.invoked.from.ide')) {
|
||||||
|
fullImplementation "com.jakewharton:butterknife-reflect:${butterKnifeVersion}"
|
||||||
|
} else {
|
||||||
|
fullImplementation "com.jakewharton:butterknife-runtime:${butterKnifeVersion}"
|
||||||
|
fullAnnotationProcessor "com.jakewharton:butterknife-compiler:${butterKnifeVersion}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
21
app/proguard-rules.pro
vendored
21
app/proguard-rules.pro
vendored
@@ -16,19 +16,24 @@
|
|||||||
# public *;
|
# public *;
|
||||||
#}
|
#}
|
||||||
|
|
||||||
# Don't obfuscate, we are open source anyway :)
|
|
||||||
-dontobfuscate
|
|
||||||
|
|
||||||
# BouncyCastle
|
# BouncyCastle
|
||||||
-keep class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; }
|
-keep,allowoptimization class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; }
|
||||||
-keep class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; }
|
-keep,allowoptimization class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; }
|
||||||
-keep class org.bouncycastle.jcajce.provider.digest.SHA1** { *; }
|
-keep,allowoptimization class org.bouncycastle.jcajce.provider.digest.SHA1** { *; }
|
||||||
-dontwarn javax.naming.**
|
-dontwarn javax.naming.**
|
||||||
|
|
||||||
# Gson
|
# Snet extention
|
||||||
-keepattributes Signature
|
-keepclassmembers class com.topjohnwu.magisk.utils.ISafetyNetHelper { *; }
|
||||||
|
|
||||||
|
# Fast Android Networking Library
|
||||||
|
-dontwarn okhttp3.**
|
||||||
|
|
||||||
# Strip logging
|
# Strip logging
|
||||||
-assumenosideeffects class com.topjohnwu.magisk.utils.Logger {
|
-assumenosideeffects class com.topjohnwu.magisk.utils.Logger {
|
||||||
public *** debug(...);
|
public *** debug(...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Excessive obfuscation
|
||||||
|
-repackageclasses 'a'
|
||||||
|
-allowaccessmodification
|
||||||
|
-optimizationpasses 6
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="com.topjohnwu.magisk">
|
package="com.topjohnwu.magisk">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
@@ -8,15 +9,18 @@
|
|||||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MagiskManager"
|
android:name="a.q"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme"
|
||||||
|
tools:ignore="GoogleAppIndexingWarning">
|
||||||
|
|
||||||
|
<!-- Activities -->
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name="a.b"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:exported="true" />
|
android:exported="true" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".SplashActivity"
|
android:name="a.c"
|
||||||
android:configChanges="orientation|screenSize"
|
android:configChanges="orientation|screenSize"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:theme="@style/SplashTheme">
|
android:theme="@style/SplashTheme">
|
||||||
@@ -26,53 +30,64 @@
|
|||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".AboutActivity"
|
android:name="a.d"
|
||||||
android:theme="@style/AppTheme.StatusBar" />
|
android:theme="@style/AppTheme.StatusBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".DonationActivity"
|
android:name="a.e"
|
||||||
android:theme="@style/AppTheme.StatusBar"/>
|
android:theme="@style/AppTheme.StatusBar"/>
|
||||||
<activity
|
<activity
|
||||||
android:name=".FlashActivity"
|
android:name="a.f"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
android:screenOrientation="nosensor"
|
android:screenOrientation="nosensor"
|
||||||
android:theme="@style/AppTheme.StatusBar" />
|
android:theme="@style/AppTheme.StatusBar" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".NoUIActivity"
|
android:name="a.g"
|
||||||
android:theme="@style/AppTheme.Translucent" />
|
android:theme="@style/AppTheme.Translucent" />
|
||||||
|
|
||||||
|
<!-- Superuser -->
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".superuser.RequestActivity"
|
android:name="a.m"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:taskAffinity="internal.superuser"
|
android:taskAffinity="internal.superuser"
|
||||||
android:theme="@style/SuRequest" />
|
android:theme="@style/SuRequest" />
|
||||||
|
|
||||||
|
<activity
|
||||||
|
android:name=".superuser.RequestActivity"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:launchMode="singleTask"
|
||||||
|
android:taskAffinity="internal.superuser"
|
||||||
|
android:theme="@style/AppTheme.Translucent" />
|
||||||
|
|
||||||
<receiver android:name=".superuser.SuReceiver" />
|
<receiver android:name=".superuser.SuReceiver" />
|
||||||
<receiver android:name=".receivers.BootReceiver">
|
|
||||||
|
<!-- Receiver -->
|
||||||
|
|
||||||
|
<receiver android:name="a.h">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
|
||||||
<receiver android:name=".receivers.PackageReceiver">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
<action android:name="android.intent.action.PACKAGE_REPLACED" />
|
||||||
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
|
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
|
||||||
<data android:scheme="package" />
|
<data android:scheme="package" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
<receiver android:name=".receivers.ManagerUpdate" />
|
<receiver android:name="a.i">
|
||||||
<receiver android:name=".receivers.RebootReceiver" />
|
|
||||||
<receiver android:name=".receivers.ShortcutReceiver">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
<action android:name="android.intent.action.LOCALE_CHANGED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<!-- Service -->
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".services.OnBootService"
|
android:name="a.j"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||||
<service
|
<service
|
||||||
android:name=".services.UpdateCheckService"
|
android:name="a.k"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||||
|
|
||||||
|
10
app/src/full/java/a/a.java
Normal file
10
app/src/full/java/a/a.java
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.utils.BootSigner;
|
||||||
|
|
||||||
|
import androidx.annotation.Keep;
|
||||||
|
|
||||||
|
@Keep
|
||||||
|
public class a extends BootSigner {
|
||||||
|
/* stub */
|
||||||
|
}
|
7
app/src/full/java/a/b.java
Normal file
7
app/src/full/java/a/b.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MainActivity;
|
||||||
|
|
||||||
|
public class b extends MainActivity {
|
||||||
|
/* stub */
|
||||||
|
}
|
7
app/src/full/java/a/c.java
Normal file
7
app/src/full/java/a/c.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.SplashActivity;
|
||||||
|
|
||||||
|
public class c extends SplashActivity {
|
||||||
|
/* stub */
|
||||||
|
}
|
7
app/src/full/java/a/d.java
Normal file
7
app/src/full/java/a/d.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.AboutActivity;
|
||||||
|
|
||||||
|
public class d extends AboutActivity {
|
||||||
|
/* stub */
|
||||||
|
}
|
7
app/src/full/java/a/e.java
Normal file
7
app/src/full/java/a/e.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.DonationActivity;
|
||||||
|
|
||||||
|
public class e extends DonationActivity {
|
||||||
|
/* stub */
|
||||||
|
}
|
7
app/src/full/java/a/f.java
Normal file
7
app/src/full/java/a/f.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
|
|
||||||
|
public class f extends FlashActivity {
|
||||||
|
/* stub */
|
||||||
|
}
|
7
app/src/full/java/a/g.java
Normal file
7
app/src/full/java/a/g.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.NoUIActivity;
|
||||||
|
|
||||||
|
public class g extends NoUIActivity {
|
||||||
|
/* stub */
|
||||||
|
}
|
7
app/src/full/java/a/h.java
Normal file
7
app/src/full/java/a/h.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.receivers.GeneralReceiver;
|
||||||
|
|
||||||
|
public class h extends GeneralReceiver {
|
||||||
|
/* stub */
|
||||||
|
}
|
7
app/src/full/java/a/i.java
Normal file
7
app/src/full/java/a/i.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
|
||||||
|
|
||||||
|
public class i extends ShortcutReceiver {
|
||||||
|
/* stub */
|
||||||
|
}
|
7
app/src/full/java/a/j.java
Normal file
7
app/src/full/java/a/j.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.services.OnBootService;
|
||||||
|
|
||||||
|
public class j extends OnBootService {
|
||||||
|
/* stub */
|
||||||
|
}
|
7
app/src/full/java/a/k.java
Normal file
7
app/src/full/java/a/k.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.services.UpdateCheckService;
|
||||||
|
|
||||||
|
public class k extends UpdateCheckService {
|
||||||
|
/* stub */
|
||||||
|
}
|
22
app/src/full/java/a/l.java
Normal file
22
app/src/full/java/a/l.java
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.components.AboutCardRow;
|
||||||
|
|
||||||
|
public class l extends AboutCardRow {
|
||||||
|
/* stub */
|
||||||
|
|
||||||
|
public l(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public l(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public l(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
}
|
7
app/src/full/java/a/m.java
Normal file
7
app/src/full/java/a/m.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.SuRequestActivity;
|
||||||
|
|
||||||
|
public class m extends SuRequestActivity {
|
||||||
|
/* stub */
|
||||||
|
}
|
7
app/src/full/java/a/q.java
Normal file
7
app/src/full/java/a/q.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
|
||||||
|
public class q extends MagiskManager {
|
||||||
|
/* stub */
|
||||||
|
}
|
@@ -2,9 +2,6 @@ package com.topjohnwu.magisk;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
@@ -15,8 +12,10 @@ import com.topjohnwu.magisk.utils.Utils;
|
|||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class AboutActivity extends BaseActivity {
|
public class AboutActivity extends BaseActivity {
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@ public class AboutActivity extends BaseActivity {
|
|||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_about);
|
setContentView(R.layout.activity_about);
|
||||||
ButterKnife.bind(this);
|
new AboutActivity_ViewBinding(this);
|
||||||
|
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
toolbar.setNavigationOnClickListener(view -> finish());
|
toolbar.setNavigationOnClickListener(view -> finish());
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.os.Environment;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -9,7 +10,6 @@ import java.util.List;
|
|||||||
public class Const {
|
public class Const {
|
||||||
|
|
||||||
public static final String DEBUG_TAG = "MagiskManager";
|
public static final String DEBUG_TAG = "MagiskManager";
|
||||||
public static final String ORIG_PKG_NAME = BuildConfig.APPLICATION_ID;
|
|
||||||
public static final String MAGISKHIDE_PROP = "persist.magisk.hide";
|
public static final String MAGISKHIDE_PROP = "persist.magisk.hide";
|
||||||
|
|
||||||
// APK content
|
// APK content
|
||||||
@@ -18,30 +18,28 @@ public class Const {
|
|||||||
public static final String SU_KEYSTORE_KEY = "su_key";
|
public static final String SU_KEYSTORE_KEY = "su_key";
|
||||||
|
|
||||||
// Paths
|
// Paths
|
||||||
public static File MAGISK_PATH;
|
public static final String MAGISK_PATH = "/sbin/.magisk/img";
|
||||||
|
public static final File EXTERNAL_PATH;
|
||||||
public static File MAGISK_DISABLE_FILE;
|
public static File MAGISK_DISABLE_FILE;
|
||||||
public static File MAGISK_HOST_FILE;
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
/* Prevent crashing on unrooted devices */
|
MAGISK_DISABLE_FILE = new File("xxx");
|
||||||
MAGISK_PATH = MAGISK_DISABLE_FILE = MAGISK_HOST_FILE = new File("xxx");
|
EXTERNAL_PATH = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
|
||||||
|
EXTERNAL_PATH.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final String BUSYBOX_PATH = "/sbin/.core/busybox";
|
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 int MIN_MODULE_VER() {
|
public static final int SNET_EXT_VER = 12;
|
||||||
return Data.magiskVersionCode >= MAGISK_VER.REMOVE_LEGACY_LINK ? 1500 : 1400;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* A list of apps that should not be shown as hide-able */
|
/* A list of apps that should not be shown as hide-able */
|
||||||
public static final List<String> HIDE_BLACKLIST = Arrays.asList(
|
public static final List<String> HIDE_BLACKLIST = Arrays.asList(
|
||||||
"android",
|
|
||||||
Data.MM().getPackageName(),
|
Data.MM().getPackageName(),
|
||||||
"com.google.android.gms"
|
"com.google.android.gms"
|
||||||
);
|
);
|
||||||
@@ -49,15 +47,11 @@ public class Const {
|
|||||||
public static final int USER_ID = Process.myUid() / 100000;
|
public static final int USER_ID = Process.myUid() / 100000;
|
||||||
|
|
||||||
public static final class MAGISK_VER {
|
public static final class MAGISK_VER {
|
||||||
public static final int UNIFIED = 1300;
|
|
||||||
public static final int FBE_AWARE = 1410;
|
|
||||||
public static final int RESETPROP_PERSIST = 1436;
|
|
||||||
public static final int MANAGER_HIDE = 1440;
|
|
||||||
public static final int HIDDEN_PATH = 1460;
|
|
||||||
public static final int REMOVE_LEGACY_LINK = 1630;
|
|
||||||
public static final int SEPOL_REFACTOR = 1640;
|
public static final int SEPOL_REFACTOR = 1640;
|
||||||
public static final int FIX_ENV = 1650;
|
public static final int FIX_ENV = 1650;
|
||||||
public static final int DBVER_SIX = 17000;
|
public static final int DBVER_SIX = 17000;
|
||||||
|
public static final int CMDLINE_DB = 17305;
|
||||||
|
public static final int HIDE_STATUS = 17315;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ID {
|
public static class ID {
|
||||||
@@ -70,20 +64,23 @@ public class Const {
|
|||||||
public static final int MAGISK_UPDATE_NOTIFICATION_ID = 4;
|
public static final int MAGISK_UPDATE_NOTIFICATION_ID = 4;
|
||||||
public static final int APK_UPDATE_NOTIFICATION_ID = 5;
|
public static final int APK_UPDATE_NOTIFICATION_ID = 5;
|
||||||
public static final int DTBO_NOTIFICATION_ID = 7;
|
public static final int DTBO_NOTIFICATION_ID = 7;
|
||||||
public static final String NOTIFICATION_CHANNEL = "magisk_notification";
|
public static final int HIDE_MANAGER_NOTIFICATION_ID = 8;
|
||||||
|
public static final String UPDATE_NOTIFICATION_CHANNEL = "update";
|
||||||
|
public static final String PROGRESS_NOTIFICATION_CHANNEL = "progress";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Url {
|
public static class Url {
|
||||||
public static final String STABLE_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/master/stable.json";
|
public static final String STABLE_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/master/stable.json";
|
||||||
public static final String BETA_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/master/beta.json";
|
public static final String BETA_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/master/beta.json";
|
||||||
public static final String REPO_URL = "https://api.github.com/users/Magisk-Modules-Repo/repos?per_page=100&sort=pushed&page=%d";
|
public static final String REPO_URL = "https://api.github.com/users/Magisk-Modules-Repo/repos?per_page=100&sort=pushed";
|
||||||
public static final String FILE_URL = "https://raw.githubusercontent.com/Magisk-Modules-Repo/%s/master/%s";
|
public static final String FILE_URL = "https://raw.githubusercontent.com/Magisk-Modules-Repo/%s/master/%s";
|
||||||
public static final String ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip";
|
public static final String ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip";
|
||||||
public static final String PAYPAL_URL = "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=CC7FZ7526MNGG";
|
public static final String PAYPAL_URL = "https://www.paypal.me/topjohnwu";
|
||||||
public static final String PATREON_URL = "https://www.patreon.com/topjohnwu";
|
public static final String PATREON_URL = "https://www.patreon.com/topjohnwu";
|
||||||
public static final String TWITTER_URL = "https://twitter.com/topjohnwu";
|
public static final String TWITTER_URL = "https://twitter.com/topjohnwu";
|
||||||
public static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382";
|
public static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382";
|
||||||
public static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/Magisk";
|
public static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/Magisk";
|
||||||
|
public static final String SNET_URL = "https://raw.githubusercontent.com/topjohnwu/magisk_files/b66b1a914978e5f4c4bbfd74a59f4ad371bac107/snet.apk";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -101,10 +98,12 @@ public class Const {
|
|||||||
|
|
||||||
// intents
|
// intents
|
||||||
public static final String OPEN_SECTION = "section";
|
public static final String OPEN_SECTION = "section";
|
||||||
public static final String INTENT_SET_FILENAME = "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 FLASH_SET_BOOT = "boot";
|
||||||
|
public static final String BROADCAST_MANAGER_UPDATE = "manager_update";
|
||||||
|
public static final String BROADCAST_REBOOT = "reboot";
|
||||||
|
|
||||||
// others
|
// others
|
||||||
public static final String CHECK_UPDATES = "check_update";
|
public static final String CHECK_UPDATES = "check_update";
|
||||||
@@ -141,9 +140,6 @@ public class Const {
|
|||||||
public static final int NAMESPACE_MODE_ISOLATE = 2;
|
public static final int NAMESPACE_MODE_ISOLATE = 2;
|
||||||
public static final int NO_NOTIFICATION = 0;
|
public static final int NO_NOTIFICATION = 0;
|
||||||
public static final int NOTIFICATION_TOAST = 1;
|
public static final int NOTIFICATION_TOAST = 1;
|
||||||
public static final int NOTIFY_NORMAL_LOG = 0;
|
|
||||||
public static final int NOTIFY_USER_TOASTS = 1;
|
|
||||||
public static final int NOTIFY_USER_TO_OWNER = 2;
|
|
||||||
public static final int SU_PROMPT = 0;
|
public static final int SU_PROMPT = 0;
|
||||||
public static final int SU_AUTO_DENY = 1;
|
public static final int SU_AUTO_DENY = 1;
|
||||||
public static final int SU_AUTO_ALLOW = 2;
|
public static final int SU_AUTO_ALLOW = 2;
|
||||||
|
@@ -5,7 +5,11 @@ import android.os.Handler;
|
|||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
import android.util.Xml;
|
import android.util.Xml;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
import com.topjohnwu.magisk.components.AboutCardRow;
|
||||||
|
import com.topjohnwu.magisk.receivers.GeneralReceiver;
|
||||||
|
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
|
||||||
|
import com.topjohnwu.magisk.services.OnBootService;
|
||||||
|
import com.topjohnwu.magisk.services.UpdateCheckService;
|
||||||
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;
|
||||||
@@ -18,11 +22,14 @@ import org.xmlpull.v1.XmlPullParserException;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
public class Data {
|
public class Data {
|
||||||
// Global app instance
|
// Global app instance
|
||||||
public static WeakReference<MagiskManager> weakApp;
|
public static WeakReference<MagiskManager> weakApp;
|
||||||
public static Handler mainHandler = new Handler(Looper.getMainLooper());
|
public static Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
public static Map<Class, Class> classMap = new HashMap<>();
|
||||||
|
|
||||||
// Current status
|
// Current status
|
||||||
public static String magiskVersionString;
|
public static String magiskVersionString;
|
||||||
@@ -40,8 +47,6 @@ public class Data {
|
|||||||
public static String managerLink;
|
public static String managerLink;
|
||||||
public static String managerNoteLink;
|
public static String managerNoteLink;
|
||||||
public static String uninstallerLink;
|
public static String uninstallerLink;
|
||||||
public static int snetVersionCode;
|
|
||||||
public static String snetLink;
|
|
||||||
|
|
||||||
// Install flags
|
// Install flags
|
||||||
public static boolean keepVerity = false;
|
public static boolean keepVerity = false;
|
||||||
@@ -51,22 +56,39 @@ public class Data {
|
|||||||
public static boolean isDarkTheme;
|
public static boolean isDarkTheme;
|
||||||
public static int suRequestTimeout;
|
public static int suRequestTimeout;
|
||||||
public static int suLogTimeout = 14;
|
public static int suLogTimeout = 14;
|
||||||
public static int suAccessState;
|
public static int multiuserState = -1;
|
||||||
public static boolean suFingerprint;
|
|
||||||
public static int multiuserMode;
|
|
||||||
public static int suResponseType;
|
public static int suResponseType;
|
||||||
public static int suNotificationType;
|
public static int suNotificationType;
|
||||||
public static int suNamespaceMode;
|
|
||||||
public static int updateChannel;
|
public static int updateChannel;
|
||||||
public static int repoOrder;
|
public static int repoOrder;
|
||||||
|
|
||||||
|
static {
|
||||||
|
classMap.put(MagiskManager.class, a.q.class);
|
||||||
|
classMap.put(MainActivity.class, a.b.class);
|
||||||
|
classMap.put(SplashActivity.class, a.c.class);
|
||||||
|
classMap.put(AboutActivity.class, a.d.class);
|
||||||
|
classMap.put(DonationActivity.class, a.e.class);
|
||||||
|
classMap.put(FlashActivity.class, a.f.class);
|
||||||
|
classMap.put(NoUIActivity.class, a.g.class);
|
||||||
|
classMap.put(GeneralReceiver.class, a.h.class);
|
||||||
|
classMap.put(ShortcutReceiver.class, a.i.class);
|
||||||
|
classMap.put(OnBootService.class, a.j.class);
|
||||||
|
classMap.put(UpdateCheckService.class, a.k.class);
|
||||||
|
classMap.put(AboutCardRow.class, a.l.class);
|
||||||
|
classMap.put(SuRequestActivity.class, a.m.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public static void loadMagiskInfo() {
|
public static void loadMagiskInfo() {
|
||||||
try {
|
try {
|
||||||
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
|
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
|
||||||
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
|
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
|
||||||
String s = ShellUtils.fastCmd((magiskVersionCode >= Const.MAGISK_VER.RESETPROP_PERSIST ?
|
if (magiskVersionCode >= Const.MAGISK_VER.HIDE_STATUS) {
|
||||||
"resetprop -p " : "getprop ") + Const.MAGISKHIDE_PROP);
|
magiskHide = Shell.su("magiskhide --status").exec().isSuccess();
|
||||||
magiskHide = s.isEmpty() || Integer.parseInt(s) != 0;
|
} else {
|
||||||
|
String s = ShellUtils.fastCmd(("resetprop -p ") + Const.MAGISKHIDE_PROP);
|
||||||
|
magiskHide = s.isEmpty() || Integer.parseInt(s) != 0;
|
||||||
|
}
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,12 +102,12 @@ public class Data {
|
|||||||
mm.prefs.edit().commit();
|
mm.prefs.edit().commit();
|
||||||
File xml = new File(mm.getFilesDir().getParent() + "/shared_prefs",
|
File xml = new File(mm.getFilesDir().getParent() + "/shared_prefs",
|
||||||
mm.getPackageName() + "_preferences.xml");
|
mm.getPackageName() + "_preferences.xml");
|
||||||
Shell.su(Utils.fmt("for usr in /data/user/*; do cat %s > ${usr}/%s; done", xml, Const.MANAGER_CONFIGS)).exec();
|
Shell.su(Utils.fmt("cat %s > /data/user/0/%s", xml, Const.MANAGER_CONFIGS)).exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void importPrefs() {
|
public static void importPrefs() {
|
||||||
MagiskManager mm = MM();
|
MagiskManager mm = MM();
|
||||||
SuFile config = new SuFile(Utils.fmt("/data/user/%d/%s", Const.USER_ID, Const.MANAGER_CONFIGS));
|
SuFile config = new SuFile("/data/user/0/" + Const.MANAGER_CONFIGS);
|
||||||
if (config.exists()) {
|
if (config.exists()) {
|
||||||
SharedPreferences.Editor editor = mm.prefs.edit();
|
SharedPreferences.Editor editor = mm.prefs.edit();
|
||||||
try {
|
try {
|
||||||
@@ -150,15 +172,6 @@ public class Data {
|
|||||||
suRequestTimeout = Utils.getPrefsInt(mm.prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]);
|
suRequestTimeout = Utils.getPrefsInt(mm.prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]);
|
||||||
suResponseType = Utils.getPrefsInt(mm.prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT);
|
suResponseType = Utils.getPrefsInt(mm.prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT);
|
||||||
suNotificationType = Utils.getPrefsInt(mm.prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST);
|
suNotificationType = Utils.getPrefsInt(mm.prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST);
|
||||||
suAccessState = mm.mDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
|
|
||||||
multiuserMode = mm.mDB.getSettings(Const.Key.SU_MULTIUSER_MODE, Const.Value.MULTIUSER_MODE_OWNER_ONLY);
|
|
||||||
suNamespaceMode = mm.mDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
|
|
||||||
suFingerprint = mm.mDB.getSettings(Const.Key.SU_FINGERPRINT, 0) != 0;
|
|
||||||
if (suFingerprint && !FingerprintHelper.canUseFingerprint()) {
|
|
||||||
// User revoked the fingerprint
|
|
||||||
mm.mDB.setSettings(Const.Key.SU_FINGERPRINT, 0);
|
|
||||||
suFingerprint = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// config
|
// config
|
||||||
isDarkTheme = mm.prefs.getBoolean(Const.Key.DARK_THEME, false);
|
isDarkTheme = mm.prefs.getBoolean(Const.Key.DARK_THEME, false);
|
||||||
@@ -170,15 +183,10 @@ public class Data {
|
|||||||
MM().prefs.edit()
|
MM().prefs.edit()
|
||||||
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
|
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
|
||||||
.putBoolean(Const.Key.MAGISKHIDE, magiskHide)
|
.putBoolean(Const.Key.MAGISKHIDE, magiskHide)
|
||||||
.putBoolean(Const.Key.HOSTS, Const.MAGISK_HOST_FILE.exists())
|
|
||||||
.putBoolean(Const.Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
|
.putBoolean(Const.Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
|
||||||
.putBoolean(Const.Key.SU_FINGERPRINT, suFingerprint)
|
|
||||||
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(suRequestTimeout))
|
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(suRequestTimeout))
|
||||||
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType))
|
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType))
|
||||||
.putString(Const.Key.SU_NOTIFICATION, String.valueOf(suNotificationType))
|
.putString(Const.Key.SU_NOTIFICATION, String.valueOf(suNotificationType))
|
||||||
.putString(Const.Key.ROOT_ACCESS, String.valueOf(suAccessState))
|
|
||||||
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(multiuserMode))
|
|
||||||
.putString(Const.Key.SU_MNT_NS, String.valueOf(suNamespaceMode))
|
|
||||||
.putString(Const.Key.UPDATE_CHANNEL, String.valueOf(updateChannel))
|
.putString(Const.Key.UPDATE_CHANNEL, String.valueOf(updateChannel))
|
||||||
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER)
|
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER)
|
||||||
.putInt(Const.Key.REPO_ORDER, repoOrder)
|
.putInt(Const.Key.REPO_ORDER, repoOrder)
|
||||||
|
@@ -2,16 +2,15 @@ package com.topjohnwu.magisk;
|
|||||||
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.components.AboutCardRow;
|
import com.topjohnwu.magisk.components.AboutCardRow;
|
||||||
import com.topjohnwu.magisk.components.BaseActivity;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class DonationActivity extends BaseActivity {
|
public class DonationActivity extends BaseActivity {
|
||||||
|
|
||||||
@@ -28,7 +27,7 @@ public class DonationActivity extends BaseActivity {
|
|||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_donation);
|
setContentView(R.layout.activity_donation);
|
||||||
ButterKnife.bind(this);
|
new DonationActivity_ViewBinding(this);
|
||||||
|
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
toolbar.setNavigationOnClickListener(view -> finish());
|
toolbar.setNavigationOnClickListener(view -> finish());
|
||||||
|
@@ -4,8 +4,6 @@ import android.Manifest;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@@ -17,7 +15,6 @@ import android.widget.Toast;
|
|||||||
import com.topjohnwu.magisk.asyncs.FlashZip;
|
import com.topjohnwu.magisk.asyncs.FlashZip;
|
||||||
import com.topjohnwu.magisk.asyncs.InstallMagisk;
|
import com.topjohnwu.magisk.asyncs.InstallMagisk;
|
||||||
import com.topjohnwu.magisk.components.BaseActivity;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
import com.topjohnwu.magisk.utils.Download;
|
|
||||||
import com.topjohnwu.magisk.utils.RootUtils;
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.CallbackList;
|
import com.topjohnwu.superuser.CallbackList;
|
||||||
@@ -31,8 +28,9 @@ import java.util.Calendar;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.OnClick;
|
import butterknife.OnClick;
|
||||||
|
|
||||||
public class FlashActivity extends BaseActivity {
|
public class FlashActivity extends BaseActivity {
|
||||||
@@ -45,11 +43,6 @@ public class FlashActivity extends BaseActivity {
|
|||||||
|
|
||||||
private List<String> logs;
|
private List<String> logs;
|
||||||
|
|
||||||
@OnClick(R.id.no_thanks)
|
|
||||||
void dismiss() {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClick(R.id.reboot)
|
@OnClick(R.id.reboot)
|
||||||
void reboot() {
|
void reboot() {
|
||||||
Shell.su("/system/bin/reboot").submit();
|
Shell.su("/system/bin/reboot").submit();
|
||||||
@@ -65,7 +58,7 @@ public class FlashActivity extends BaseActivity {
|
|||||||
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
||||||
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
|
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
|
||||||
|
|
||||||
File logFile = new File(Download.EXTERNAL_PATH, filename);
|
File logFile = new File(Const.EXTERNAL_PATH, filename);
|
||||||
try (FileWriter writer = new FileWriter(logFile)) {
|
try (FileWriter writer = new FileWriter(logFile)) {
|
||||||
for (String s : logs) {
|
for (String s : logs) {
|
||||||
writer.write(s);
|
writer.write(s);
|
||||||
@@ -88,7 +81,8 @@ public class FlashActivity extends BaseActivity {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_flash);
|
setContentView(R.layout.activity_flash);
|
||||||
ButterKnife.bind(this);
|
new FlashActivity_ViewBinding(this);
|
||||||
|
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
ActionBar ab = getSupportActionBar();
|
ActionBar ab = getSupportActionBar();
|
||||||
if (ab != null) {
|
if (ab != null) {
|
||||||
@@ -145,6 +139,12 @@ public class FlashActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.close)
|
||||||
|
@Override
|
||||||
|
public void finish() {
|
||||||
|
super.finish();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
// Prevent user accidentally press back button
|
// Prevent user accidentally press back button
|
||||||
|
@@ -1,12 +1,10 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
|
import com.topjohnwu.magisk.database.MagiskDB;
|
||||||
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
||||||
import com.topjohnwu.magisk.utils.LocaleManager;
|
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||||
import com.topjohnwu.magisk.utils.RootUtils;
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
@@ -22,7 +20,7 @@ public class MagiskManager extends ContainerApp {
|
|||||||
|
|
||||||
// Global resources
|
// Global resources
|
||||||
public SharedPreferences prefs;
|
public SharedPreferences prefs;
|
||||||
public MagiskDatabaseHelper mDB;
|
public MagiskDB mDB;
|
||||||
public RepoDatabaseHelper repoDB;
|
public RepoDatabaseHelper repoDB;
|
||||||
|
|
||||||
public MagiskManager() {
|
public MagiskManager() {
|
||||||
@@ -36,22 +34,11 @@ public class MagiskManager extends ContainerApp {
|
|||||||
Shell.Config.setFlags(Shell.FLAG_MOUNT_MASTER);
|
Shell.Config.setFlags(Shell.FLAG_MOUNT_MASTER);
|
||||||
Shell.Config.verboseLogging(BuildConfig.DEBUG);
|
Shell.Config.verboseLogging(BuildConfig.DEBUG);
|
||||||
Shell.Config.setInitializer(RootUtils.class);
|
Shell.Config.setInitializer(RootUtils.class);
|
||||||
|
Shell.Config.setTimeout(2);
|
||||||
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
mDB = MagiskDatabaseHelper.getInstance(this);
|
mDB = MagiskDB.getInstance();
|
||||||
|
repoDB = new RepoDatabaseHelper(this);
|
||||||
String pkg = mDB.getStrings(Const.Key.SU_MANAGER, null);
|
|
||||||
if (pkg != null && getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
|
||||||
mDB.setStrings(Const.Key.SU_MANAGER, null);
|
|
||||||
Shell.su("pm uninstall " + pkg).exec();
|
|
||||||
}
|
|
||||||
if (TextUtils.equals(pkg, getPackageName())) {
|
|
||||||
try {
|
|
||||||
// We are the manager, remove com.topjohnwu.magisk as it could be malware
|
|
||||||
getPackageManager().getApplicationInfo(Const.ORIG_PKG_NAME, 0);
|
|
||||||
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME);
|
|
||||||
} catch (PackageManager.NameNotFoundException ignored) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
LocaleManager.setLocale(this);
|
LocaleManager.setLocale(this);
|
||||||
Data.loadConfig();
|
Data.loadConfig();
|
||||||
|
@@ -3,17 +3,11 @@ package com.topjohnwu.magisk;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.design.widget.NavigationView;
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.app.FragmentTransaction;
|
|
||||||
import android.support.v4.widget.DrawerLayout;
|
|
||||||
import android.support.v7.app.ActionBarDrawerToggle;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.google.android.material.navigation.NavigationView;
|
||||||
import com.topjohnwu.magisk.components.BaseActivity;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
import com.topjohnwu.magisk.fragments.LogFragment;
|
import com.topjohnwu.magisk.fragments.LogFragment;
|
||||||
import com.topjohnwu.magisk.fragments.MagiskFragment;
|
import com.topjohnwu.magisk.fragments.MagiskFragment;
|
||||||
@@ -24,10 +18,16 @@ import com.topjohnwu.magisk.fragments.SettingsFragment;
|
|||||||
import com.topjohnwu.magisk.fragments.SuperuserFragment;
|
import com.topjohnwu.magisk.fragments.SuperuserFragment;
|
||||||
import com.topjohnwu.magisk.utils.Download;
|
import com.topjohnwu.magisk.utils.Download;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
|
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 butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class MainActivity extends BaseActivity
|
public class MainActivity extends BaseActivity
|
||||||
implements NavigationView.OnNavigationItemSelectedListener, Topic.Subscriber {
|
implements NavigationView.OnNavigationItemSelectedListener, Topic.Subscriber {
|
||||||
@@ -36,9 +36,9 @@ public class MainActivity extends BaseActivity
|
|||||||
private int mDrawerItem;
|
private int mDrawerItem;
|
||||||
private static boolean fromShortcut = false;
|
private static boolean fromShortcut = false;
|
||||||
|
|
||||||
@BindView(R.id.drawer_layout) DrawerLayout drawer;
|
|
||||||
@BindView(R.id.toolbar) public Toolbar toolbar;
|
@BindView(R.id.toolbar) public Toolbar toolbar;
|
||||||
@BindView(R.id.nav_view) public NavigationView navigationView;
|
@BindView(R.id.drawer_layout) DrawerLayout drawer;
|
||||||
|
@BindView(R.id.nav_view) NavigationView navigationView;
|
||||||
|
|
||||||
private float toolbarElevation;
|
private float toolbarElevation;
|
||||||
|
|
||||||
@@ -50,13 +50,13 @@ public class MainActivity extends BaseActivity
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(final Bundle savedInstanceState) {
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
if (!mm.hasInit) {
|
if (!mm.hasInit) {
|
||||||
startActivity(new Intent(this, SplashActivity.class));
|
startActivity(new Intent(this, Data.classMap.get(SplashActivity.class)));
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setContentView(R.layout.activity_main);
|
setContentView(R.layout.activity_main);
|
||||||
ButterKnife.bind(this);
|
new MainActivity_ViewBinding(this);
|
||||||
|
|
||||||
setSupportActionBar(toolbar);
|
setSupportActionBar(toolbar);
|
||||||
|
|
||||||
@@ -112,11 +112,6 @@ public class MainActivity extends BaseActivity
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public int[] getSubscribedTopics() {
|
|
||||||
return new int[] {Topic.RELOAD_ACTIVITY};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPublish(int topic, Object[] result) {
|
public void onPublish(int topic, Object[] result) {
|
||||||
recreate();
|
recreate();
|
||||||
@@ -125,14 +120,12 @@ public class MainActivity extends BaseActivity
|
|||||||
public void checkHideSection() {
|
public void checkHideSection() {
|
||||||
Menu menu = navigationView.getMenu();
|
Menu menu = navigationView.getMenu();
|
||||||
menu.findItem(R.id.magiskhide).setVisible(Shell.rootAccess() &&
|
menu.findItem(R.id.magiskhide).setVisible(Shell.rootAccess() &&
|
||||||
Data.magiskVersionCode >= Const.MAGISK_VER.UNIFIED &&
|
|
||||||
mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false));
|
mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false));
|
||||||
menu.findItem(R.id.modules).setVisible(Shell.rootAccess() && Data.magiskVersionCode >= 0);
|
menu.findItem(R.id.modules).setVisible(Shell.rootAccess() && Data.magiskVersionCode >= 0);
|
||||||
menu.findItem(R.id.downloads).setVisible(Download.checkNetworkStatus(this)
|
menu.findItem(R.id.downloads).setVisible(Download.checkNetworkStatus(this)
|
||||||
&& Shell.rootAccess() && Data.magiskVersionCode >= 0);
|
&& Shell.rootAccess() && Data.magiskVersionCode >= 0);
|
||||||
menu.findItem(R.id.log).setVisible(Shell.rootAccess());
|
menu.findItem(R.id.log).setVisible(Shell.rootAccess());
|
||||||
menu.findItem(R.id.superuser).setVisible(Shell.rootAccess() &&
|
menu.findItem(R.id.superuser).setVisible(Utils.showSuperUser());
|
||||||
!(Const.USER_ID > 0 && Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void navigate(String item) {
|
public void navigate(String item) {
|
||||||
@@ -196,11 +189,11 @@ public class MainActivity extends BaseActivity
|
|||||||
displayFragment(new SettingsFragment(), true);
|
displayFragment(new SettingsFragment(), true);
|
||||||
break;
|
break;
|
||||||
case R.id.app_about:
|
case R.id.app_about:
|
||||||
startActivity(new Intent(this, AboutActivity.class));
|
startActivity(new Intent(this, Data.classMap.get(AboutActivity.class)));
|
||||||
mDrawerItem = bak;
|
mDrawerItem = bak;
|
||||||
break;
|
break;
|
||||||
case R.id.donation:
|
case R.id.donation:
|
||||||
startActivity(new Intent(this, DonationActivity.class));
|
startActivity(new Intent(this, Data.classMap.get(DonationActivity.class)));
|
||||||
mDrawerItem = bak;
|
mDrawerItem = bak;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -1,9 +1,9 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.components.BaseActivity;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public class NoUIActivity extends BaseActivity {
|
public class NoUIActivity extends BaseActivity {
|
||||||
@Override
|
@Override
|
||||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||||
|
@@ -1,18 +1,18 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
import android.app.NotificationChannel;
|
|
||||||
import android.app.NotificationManager;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
||||||
import com.topjohnwu.magisk.asyncs.UpdateRepos;
|
import com.topjohnwu.magisk.asyncs.UpdateRepos;
|
||||||
import com.topjohnwu.magisk.components.BaseActivity;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
import com.topjohnwu.magisk.components.Notifications;
|
||||||
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
|
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
|
||||||
import com.topjohnwu.magisk.utils.Download;
|
import com.topjohnwu.magisk.utils.Download;
|
||||||
import com.topjohnwu.magisk.utils.LocaleManager;
|
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||||
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
@@ -22,6 +22,19 @@ public class SplashActivity extends BaseActivity {
|
|||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
String pkg = mm.mDB.getStrings(Const.Key.SU_MANAGER, null);
|
||||||
|
if (pkg != null && getPackageName().equals(BuildConfig.APPLICATION_ID)) {
|
||||||
|
mm.mDB.setStrings(Const.Key.SU_MANAGER, null);
|
||||||
|
Shell.su("pm uninstall " + pkg).exec();
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
RootUtils.uninstallPkg(BuildConfig.APPLICATION_ID);
|
||||||
|
} catch (PackageManager.NameNotFoundException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
// Magisk working as expected
|
// Magisk working as expected
|
||||||
if (Shell.rootAccess() && Data.magiskVersionCode > 0) {
|
if (Shell.rootAccess() && Data.magiskVersionCode > 0) {
|
||||||
// Update check service
|
// Update check service
|
||||||
@@ -30,21 +43,16 @@ public class SplashActivity extends BaseActivity {
|
|||||||
Utils.loadModules();
|
Utils.loadModules();
|
||||||
}
|
}
|
||||||
|
|
||||||
mm.repoDB = new RepoDatabaseHelper(this);
|
|
||||||
Data.importPrefs();
|
Data.importPrefs();
|
||||||
|
|
||||||
// Dynamic detect all locales
|
// Dynamic detect all locales
|
||||||
LocaleManager.loadAvailableLocales();
|
LocaleManager.loadAvailableLocales();
|
||||||
|
|
||||||
// Create notification channel on Android O
|
// Create notification channel on Android O
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
Notifications.setup(this);
|
||||||
NotificationChannel channel = new NotificationChannel(Const.ID.NOTIFICATION_CHANNEL,
|
|
||||||
getString(R.string.magisk_updates), NotificationManager.IMPORTANCE_DEFAULT);
|
|
||||||
getSystemService(NotificationManager.class).createNotificationChannel(channel);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Setup shortcuts
|
// Setup shortcuts
|
||||||
sendBroadcast(new Intent(this, ShortcutReceiver.class));
|
sendBroadcast(new Intent(this, Data.classMap.get(ShortcutReceiver.class)));
|
||||||
|
|
||||||
if (Download.checkNetworkStatus(this)) {
|
if (Download.checkNetworkStatus(this)) {
|
||||||
// Fire update check
|
// Fire update check
|
||||||
@@ -58,7 +66,7 @@ public class SplashActivity extends BaseActivity {
|
|||||||
|
|
||||||
mm.hasInit = true;
|
mm.hasInit = true;
|
||||||
|
|
||||||
Intent intent = new Intent(this, MainActivity.class);
|
Intent intent = new Intent(this, Data.classMap.get(MainActivity.class));
|
||||||
intent.putExtra(Const.Key.OPEN_SECTION, getIntent().getStringExtra(Const.Key.OPEN_SECTION));
|
intent.putExtra(Const.Key.OPEN_SECTION, getIntent().getStringExtra(Const.Key.OPEN_SECTION));
|
||||||
intent.putExtra(BaseActivity.INTENT_PERM, getIntent().getStringExtra(BaseActivity.INTENT_PERM));
|
intent.putExtra(BaseActivity.INTENT_PERM, getIntent().getStringExtra(BaseActivity.INTENT_PERM));
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
|
259
app/src/full/java/com/topjohnwu/magisk/SuRequestActivity.java
Normal file
259
app/src/full/java/com/topjohnwu/magisk/SuRequestActivity.java
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
|
import android.net.LocalSocketAddress;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.CountDownTimer;
|
||||||
|
import android.os.FileObserver;
|
||||||
|
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 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 androidx.annotation.Nullable;
|
||||||
|
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;
|
||||||
|
|
||||||
|
class SuConnectorV1 extends SuConnector {
|
||||||
|
|
||||||
|
SuConnectorV1(String name) throws IOException {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect(String name) throws IOException {
|
||||||
|
socket.connect(new LocalSocketAddress(name, LocalSocketAddress.Namespace.FILESYSTEM));
|
||||||
|
new FileObserver(name) {
|
||||||
|
@Override
|
||||||
|
public void onEvent(int fileEvent, String path) {
|
||||||
|
if (fileEvent == FileObserver.DELETE_SELF) {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.startWatching();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse() throws IOException {
|
||||||
|
out.write((policy.policy == Policy.ALLOW ? "socket:ALLOW" : "socket:DENY").getBytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SuConnectorV2 extends SuConnector {
|
||||||
|
|
||||||
|
SuConnectorV2(String name) throws IOException {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void connect(String name) throws IOException {
|
||||||
|
socket.connect(new LocalSocketAddress(name, LocalSocketAddress.Namespace.ABSTRACT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse() throws IOException {
|
||||||
|
out.writeInt(policy.policy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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();
|
||||||
|
mm.mDB.clearOutdated();
|
||||||
|
|
||||||
|
// Get policy
|
||||||
|
Intent intent = getIntent();
|
||||||
|
try {
|
||||||
|
String socketName = intent.getStringExtra("socket");
|
||||||
|
connector = intent.getIntExtra("version", 1) == 1 ?
|
||||||
|
new SuConnectorV1(socketName) : new SuConnectorV2(socketName);
|
||||||
|
Bundle bundle = connector.readSocketInput();
|
||||||
|
int uid = Integer.parseInt(bundle.getString("uid"));
|
||||||
|
policy = mm.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 (Data.suResponseType) {
|
||||||
|
case Const.Value.SU_AUTO_DENY:
|
||||||
|
handleAction(Policy.DENY, 0);
|
||||||
|
return;
|
||||||
|
case Const.Value.SU_AUTO_ALLOW:
|
||||||
|
handleAction(Policy.ALLOW, 0);
|
||||||
|
return;
|
||||||
|
case Const.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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
timer = new CountDownTimer(Data.suRequestTimeout * 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) {
|
||||||
|
handleAction(action, Const.Value.timeoutList[timeout.getSelectedItemPosition()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleAction(int action, int time) {
|
||||||
|
policy.policy = action;
|
||||||
|
if (time >= 0) {
|
||||||
|
policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60);
|
||||||
|
mm.mDB.updatePolicy(policy);
|
||||||
|
}
|
||||||
|
handleAction();
|
||||||
|
}
|
||||||
|
}
|
@@ -3,11 +3,7 @@ package com.topjohnwu.magisk.adapters;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -19,8 +15,8 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.utils.LocaleManager;
|
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -28,8 +24,9 @@ import java.util.Collections;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
|
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
|
||||||
|
|
||||||
@@ -53,25 +50,12 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
return new ViewHolder(v);
|
return new ViewHolder(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getLabel(ApplicationInfo info) {
|
|
||||||
if (info.labelRes > 0) {
|
|
||||||
try {
|
|
||||||
Resources res = pm.getResourcesForApplication(info);
|
|
||||||
Configuration config = new Configuration();
|
|
||||||
config.setLocale(LocaleManager.locale);
|
|
||||||
res.updateConfiguration(config, res.getDisplayMetrics());
|
|
||||||
return res.getString(info.labelRes);
|
|
||||||
} catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ }
|
|
||||||
}
|
|
||||||
return info.loadLabel(pm).toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadApps() {
|
private void loadApps() {
|
||||||
fullList = pm.getInstalledApplications(0);
|
fullList = pm.getInstalledApplications(0);
|
||||||
hideList = Shell.su("magiskhide --ls").exec().getOut();
|
hideList = Shell.su("magiskhide --ls").exec().getOut();
|
||||||
for (Iterator<ApplicationInfo> i = fullList.iterator(); i.hasNext(); ) {
|
for (Iterator<ApplicationInfo> i = fullList.iterator(); i.hasNext(); ) {
|
||||||
ApplicationInfo info = i.next();
|
ApplicationInfo info = i.next();
|
||||||
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled) {
|
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled || info.uid == 1000) {
|
||||||
i.remove();
|
i.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,7 +63,8 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
boolean ah = hideList.contains(a.packageName);
|
boolean ah = hideList.contains(a.packageName);
|
||||||
boolean bh = hideList.contains(b.packageName);
|
boolean bh = hideList.contains(b.packageName);
|
||||||
if (ah == bh) {
|
if (ah == bh) {
|
||||||
return getLabel(a).toLowerCase().compareTo(getLabel(b).toLowerCase());
|
return Utils.getAppLabel(a, pm).toLowerCase()
|
||||||
|
.compareTo(Utils.getAppLabel(b, pm).toLowerCase());
|
||||||
} else if (ah) {
|
} else if (ah) {
|
||||||
return -1;
|
return -1;
|
||||||
} else {
|
} else {
|
||||||
@@ -94,7 +79,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
ApplicationInfo info = showList.get(position);
|
ApplicationInfo info = showList.get(position);
|
||||||
|
|
||||||
holder.appIcon.setImageDrawable(info.loadIcon(pm));
|
holder.appIcon.setImageDrawable(info.loadIcon(pm));
|
||||||
holder.appName.setText(getLabel(info));
|
holder.appName.setText(Utils.getAppLabel(info, pm));
|
||||||
holder.appPackage.setText(info.packageName);
|
holder.appPackage.setText(info.packageName);
|
||||||
|
|
||||||
holder.checkBox.setOnCheckedChangeListener(null);
|
holder.checkBox.setOnCheckedChangeListener(null);
|
||||||
@@ -132,11 +117,11 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
|
|
||||||
ViewHolder(View itemView) {
|
ViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
ButterKnife.bind(this, itemView);
|
new ApplicationAdapter$ViewHolder_ViewBinding(this, itemView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ApplicationFilter extends Filter {
|
class ApplicationFilter extends Filter {
|
||||||
|
|
||||||
private boolean lowercaseContains(String s, CharSequence filter) {
|
private boolean lowercaseContains(String s, CharSequence filter) {
|
||||||
return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);
|
return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);
|
||||||
@@ -150,7 +135,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
showList = new ArrayList<>();
|
showList = new ArrayList<>();
|
||||||
String filter = constraint.toString().toLowerCase();
|
String filter = constraint.toString().toLowerCase();
|
||||||
for (ApplicationInfo info : fullList) {
|
for (ApplicationInfo info : fullList) {
|
||||||
if (lowercaseContains(getLabel(info), filter)
|
if (lowercaseContains(Utils.getAppLabel(info, pm), filter)
|
||||||
|| lowercaseContains(info.packageName, filter)) {
|
|| lowercaseContains(info.packageName, filter)) {
|
||||||
showList.add(info);
|
showList.add(info);
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.adapters;
|
package com.topjohnwu.magisk.adapters;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -11,6 +9,7 @@ import android.widget.CheckBox;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.container.Module;
|
import com.topjohnwu.magisk.container.Module;
|
||||||
@@ -18,8 +17,9 @@ import com.topjohnwu.superuser.Shell;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHolder> {
|
public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHolder> {
|
||||||
|
|
||||||
@@ -29,6 +29,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
|
|||||||
mList = list;
|
mList = list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false);
|
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false);
|
||||||
@@ -46,9 +47,9 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
|
|||||||
String noInfo = context.getString(R.string.no_info_provided);
|
String noInfo = context.getString(R.string.no_info_provided);
|
||||||
|
|
||||||
holder.title.setText(module.getName());
|
holder.title.setText(module.getName());
|
||||||
holder.versionName.setText( TextUtils.isEmpty(version) ? noInfo : version);
|
holder.versionName.setText(TextUtils.isEmpty(version) ? noInfo : version);
|
||||||
holder.author.setText( TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author));
|
holder.author.setText(TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author));
|
||||||
holder.description.setText( TextUtils.isEmpty(description) ? noInfo : description);
|
holder.description.setText(TextUtils.isEmpty(description) ? noInfo : description);
|
||||||
|
|
||||||
holder.checkBox.setOnCheckedChangeListener(null);
|
holder.checkBox.setOnCheckedChangeListener(null);
|
||||||
holder.checkBox.setChecked(module.isEnabled());
|
holder.checkBox.setChecked(module.isEnabled());
|
||||||
@@ -114,7 +115,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
|
|||||||
|
|
||||||
ViewHolder(View itemView) {
|
ViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
ButterKnife.bind(this, itemView);
|
new ModulesAdapter$ViewHolder_ViewBinding(this, itemView);
|
||||||
|
|
||||||
if (!Shell.rootAccess()) {
|
if (!Shell.rootAccess()) {
|
||||||
checkBox.setEnabled(false);
|
checkBox.setEnabled(false);
|
||||||
|
@@ -2,8 +2,6 @@ package com.topjohnwu.magisk.adapters;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -11,33 +9,37 @@ import android.widget.ImageView;
|
|||||||
import android.widget.Switch;
|
import android.widget.Switch;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.components.CustomAlertDialog;
|
import com.topjohnwu.magisk.components.CustomAlertDialog;
|
||||||
import com.topjohnwu.magisk.components.ExpandableView;
|
import com.topjohnwu.magisk.components.ExpandableView;
|
||||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.container.Policy;
|
import com.topjohnwu.magisk.container.Policy;
|
||||||
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
|
import com.topjohnwu.magisk.database.MagiskDB;
|
||||||
|
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> {
|
public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> {
|
||||||
|
|
||||||
private List<Policy> policyList;
|
private List<Policy> policyList;
|
||||||
private MagiskDatabaseHelper dbHelper;
|
private MagiskDB dbHelper;
|
||||||
private PackageManager pm;
|
private PackageManager pm;
|
||||||
private Set<Policy> expandList = new HashSet<>();
|
private Set<Policy> expandList = new HashSet<>();
|
||||||
|
|
||||||
public PolicyAdapter(List<Policy> list, MagiskDatabaseHelper db, PackageManager pm) {
|
public PolicyAdapter(List<Policy> list, MagiskDB db, PackageManager pm) {
|
||||||
policyList = list;
|
policyList = list;
|
||||||
dbHelper = db;
|
dbHelper = db;
|
||||||
this.pm = pm;
|
this.pm = pm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_policy, parent, false);
|
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_policy, parent, false);
|
||||||
@@ -63,14 +65,34 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
|
|||||||
holder.appName.setText(policy.appName);
|
holder.appName.setText(policy.appName);
|
||||||
holder.packageName.setText(policy.packageName);
|
holder.packageName.setText(policy.packageName);
|
||||||
holder.appIcon.setImageDrawable(policy.info.loadIcon(pm));
|
holder.appIcon.setImageDrawable(policy.info.loadIcon(pm));
|
||||||
holder.masterSwitch.setOnCheckedChangeListener((v, isChecked) -> {
|
|
||||||
if ((isChecked && policy.policy == Policy.DENY) ||
|
holder.notificationSwitch.setOnCheckedChangeListener(null);
|
||||||
(!isChecked && policy.policy == Policy.ALLOW)) {
|
holder.loggingSwitch.setOnCheckedChangeListener(null);
|
||||||
policy.policy = isChecked ? Policy.ALLOW : Policy.DENY;
|
|
||||||
String message = v.getContext().getString(
|
holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW);
|
||||||
isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName);
|
holder.notificationSwitch.setChecked(policy.notification);
|
||||||
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
|
holder.loggingSwitch.setChecked(policy.logging);
|
||||||
dbHelper.updatePolicy(policy);
|
|
||||||
|
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);
|
||||||
|
FingerprintHelper.showAuthDialog((Activity) v.getContext(), () -> {
|
||||||
|
holder.masterSwitch.setChecked(isChecked);
|
||||||
|
r.run();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
r.run();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
holder.notificationSwitch.setOnCheckedChangeListener((v, isChecked) -> {
|
holder.notificationSwitch.setOnCheckedChangeListener((v, isChecked) -> {
|
||||||
@@ -107,9 +129,6 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
|
|||||||
.setNegativeButton(R.string.no_thanks, null)
|
.setNegativeButton(R.string.no_thanks, null)
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.show());
|
.show());
|
||||||
holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW);
|
|
||||||
holder.notificationSwitch.setChecked(policy.notification);
|
|
||||||
holder.loggingSwitch.setChecked(policy.logging);
|
|
||||||
|
|
||||||
// Hide for now
|
// Hide for now
|
||||||
holder.moreInfo.setVisibility(View.GONE);
|
holder.moreInfo.setVisibility(View.GONE);
|
||||||
@@ -137,7 +156,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
|
|||||||
|
|
||||||
public ViewHolder(View itemView) {
|
public ViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
ButterKnife.bind(this, itemView);
|
new PolicyAdapter$ViewHolder_ViewBinding(this, itemView);
|
||||||
container.expandLayout = expandLayout;
|
container.expandLayout = expandLayout;
|
||||||
setupExpandable();
|
setupExpandable();
|
||||||
}
|
}
|
||||||
|
@@ -2,7 +2,6 @@ package com.topjohnwu.magisk.adapters;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
@@ -13,8 +12,8 @@ import android.widget.LinearLayout;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.asyncs.DownloadModule;
|
||||||
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
||||||
import com.topjohnwu.magisk.asyncs.ProcessRepoZip;
|
|
||||||
import com.topjohnwu.magisk.components.BaseActivity;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
import com.topjohnwu.magisk.components.CustomAlertDialog;
|
import com.topjohnwu.magisk.components.CustomAlertDialog;
|
||||||
import com.topjohnwu.magisk.container.Module;
|
import com.topjohnwu.magisk.container.Module;
|
||||||
@@ -25,8 +24,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, ReposAdapter.RepoHolder> {
|
public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, ReposAdapter.RepoHolder> {
|
||||||
|
|
||||||
@@ -89,11 +88,16 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
|
|||||||
Repo repo = repoPairs.get(section).second.get(position);
|
Repo repo = repoPairs.get(section).second.get(position);
|
||||||
Context context = holder.itemView.getContext();
|
Context context = holder.itemView.getContext();
|
||||||
|
|
||||||
holder.title.setText(repo.getName());
|
String name = repo.getName();
|
||||||
holder.versionName.setText(repo.getVersion());
|
String version = repo.getVersion();
|
||||||
String author = repo.getAuthor();
|
String author = repo.getAuthor();
|
||||||
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
|
String description = repo.getDescription();
|
||||||
holder.description.setText(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.updateTime.setText(context.getString(R.string.updated_on, repo.getLastUpdateString()));
|
||||||
|
|
||||||
holder.infoLayout.setOnClickListener(v ->
|
holder.infoLayout.setOnClickListener(v ->
|
||||||
@@ -101,16 +105,15 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
|
|||||||
|
|
||||||
holder.downloadImage.setOnClickListener(v -> {
|
holder.downloadImage.setOnClickListener(v -> {
|
||||||
new CustomAlertDialog((BaseActivity) context)
|
new CustomAlertDialog((BaseActivity) context)
|
||||||
.setTitle(context.getString(R.string.repo_install_title, repo.getName()))
|
.setTitle(context.getString(R.string.repo_install_title, repo.getName()))
|
||||||
.setMessage(context.getString(R.string.repo_install_msg, repo.getDownloadFilename()))
|
.setMessage(context.getString(R.string.repo_install_msg, repo.getDownloadFilename()))
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
.setPositiveButton(R.string.install, (d, i) ->
|
.setPositiveButton(R.string.install, (d, i) ->
|
||||||
new ProcessRepoZip((BaseActivity) context, repo, true).exec()
|
DownloadModule.exec((BaseActivity) context, repo, true))
|
||||||
)
|
.setNeutralButton(R.string.download, (d, i) ->
|
||||||
.setNeutralButton(R.string.download, (d, i) ->
|
DownloadModule.exec((BaseActivity) context, repo, false))
|
||||||
new ProcessRepoZip((BaseActivity) context, repo, false).exec())
|
.setNegativeButton(R.string.no_thanks, null)
|
||||||
.setNegativeButton(R.string.no_thanks, null)
|
.show();
|
||||||
.show();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +168,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
|
|||||||
|
|
||||||
SectionHolder(View itemView) {
|
SectionHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
ButterKnife.bind(this, itemView);
|
new ReposAdapter$SectionHolder_ViewBinding(this, itemView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +184,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
|
|||||||
|
|
||||||
RepoHolder(View itemView) {
|
RepoHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
ButterKnife.bind(this, itemView);
|
new ReposAdapter$RepoHolder_ViewBinding(this, itemView);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -1,15 +1,18 @@
|
|||||||
package com.topjohnwu.magisk.adapters;
|
package com.topjohnwu.magisk.adapters;
|
||||||
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.ViewGroup;
|
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>
|
public abstract class SectionedAdapter<S extends RecyclerView.ViewHolder, C extends RecyclerView.ViewHolder>
|
||||||
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
|
||||||
|
|
||||||
private static final int SECTION_TYPE = Integer.MIN_VALUE;
|
private static final int SECTION_TYPE = Integer.MIN_VALUE;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
final public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
final public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
if (viewType == SECTION_TYPE)
|
if (viewType == SECTION_TYPE)
|
||||||
return onCreateSectionViewHolder(parent);
|
return onCreateSectionViewHolder(parent);
|
||||||
return onCreateItemViewHolder(parent, viewType);
|
return onCreateItemViewHolder(parent, viewType);
|
||||||
@@ -17,7 +20,7 @@ public abstract class SectionedAdapter<S extends RecyclerView.ViewHolder, C exte
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
final public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
|
final public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
|
||||||
PositionInfo info = getPositionInfo(position);
|
PositionInfo info = getPositionInfo(position);
|
||||||
if (info.position == -1)
|
if (info.position == -1)
|
||||||
onBindSectionViewHolder((S) holder, info.section);
|
onBindSectionViewHolder((S) holder, info.section);
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
package com.topjohnwu.magisk.adapters;
|
package com.topjohnwu.magisk.adapters;
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -13,38 +11,37 @@ import android.widget.TextView;
|
|||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.components.ExpandableView;
|
import com.topjohnwu.magisk.components.ExpandableView;
|
||||||
import com.topjohnwu.magisk.container.SuLogEntry;
|
import com.topjohnwu.magisk.container.SuLogEntry;
|
||||||
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
|
import com.topjohnwu.magisk.database.MagiskDB;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, SuLogAdapter.LogViewHolder> {
|
public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, SuLogAdapter.LogViewHolder> {
|
||||||
|
|
||||||
private List<List<Integer>> logEntryList;
|
private List<List<SuLogEntry>> logEntries;
|
||||||
private Set<Integer> itemExpanded, sectionExpanded;
|
private Set<Integer> itemExpanded, sectionExpanded;
|
||||||
private MagiskDatabaseHelper suDB;
|
private MagiskDB suDB;
|
||||||
private Cursor suLogCursor = null;
|
|
||||||
|
|
||||||
public SuLogAdapter(MagiskDatabaseHelper db) {
|
public SuLogAdapter(MagiskDB db) {
|
||||||
suDB = db;
|
suDB = db;
|
||||||
logEntryList = Collections.emptyList();
|
logEntries = Collections.emptyList();
|
||||||
sectionExpanded = new HashSet<>();
|
sectionExpanded = new HashSet<>();
|
||||||
itemExpanded = new HashSet<>();
|
itemExpanded = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSectionCount() {
|
public int getSectionCount() {
|
||||||
return logEntryList.size();
|
return logEntries.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getItemCount(int section) {
|
public int getItemCount(int section) {
|
||||||
return sectionExpanded.contains(section) ? logEntryList.get(section).size() : 0;
|
return sectionExpanded.contains(section) ? logEntries.get(section).size() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -61,8 +58,7 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindSectionViewHolder(SectionHolder holder, int section) {
|
public void onBindSectionViewHolder(SectionHolder holder, int section) {
|
||||||
suLogCursor.moveToPosition(logEntryList.get(section).get(0));
|
SuLogEntry entry = logEntries.get(section).get(0);
|
||||||
SuLogEntry entry = new SuLogEntry(suLogCursor);
|
|
||||||
holder.arrow.setRotation(sectionExpanded.contains(section) ? 180 : 0);
|
holder.arrow.setRotation(sectionExpanded.contains(section) ? 180 : 0);
|
||||||
holder.itemView.setOnClickListener(v -> {
|
holder.itemView.setOnClickListener(v -> {
|
||||||
RotateAnimation rotate;
|
RotateAnimation rotate;
|
||||||
@@ -70,11 +66,11 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
|
|||||||
holder.arrow.setRotation(0);
|
holder.arrow.setRotation(0);
|
||||||
rotate = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
rotate = new RotateAnimation(180, 0, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
||||||
sectionExpanded.remove(section);
|
sectionExpanded.remove(section);
|
||||||
notifyItemRangeRemoved(getItemPosition(section, 0), logEntryList.get(section).size());
|
notifyItemRangeRemoved(getItemPosition(section, 0), logEntries.get(section).size());
|
||||||
} else {
|
} else {
|
||||||
rotate = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
rotate = new RotateAnimation(0, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
|
||||||
sectionExpanded.add(section);
|
sectionExpanded.add(section);
|
||||||
notifyItemRangeInserted(getItemPosition(section, 0), logEntryList.get(section).size());
|
notifyItemRangeInserted(getItemPosition(section, 0), logEntries.get(section).size());
|
||||||
}
|
}
|
||||||
rotate.setDuration(300);
|
rotate.setDuration(300);
|
||||||
rotate.setFillAfter(true);
|
rotate.setFillAfter(true);
|
||||||
@@ -85,17 +81,16 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindItemViewHolder(LogViewHolder holder, int section, int position) {
|
public void onBindItemViewHolder(LogViewHolder holder, int section, int position) {
|
||||||
int sqlPosition = logEntryList.get(section).get(position);
|
SuLogEntry entry = logEntries.get(section).get(position);
|
||||||
suLogCursor.moveToPosition(sqlPosition);
|
int realIdx = getItemPosition(section, position);
|
||||||
SuLogEntry entry = new SuLogEntry(suLogCursor);
|
holder.setExpanded(itemExpanded.contains(realIdx));
|
||||||
holder.setExpanded(itemExpanded.contains(sqlPosition));
|
|
||||||
holder.itemView.setOnClickListener(view -> {
|
holder.itemView.setOnClickListener(view -> {
|
||||||
if (holder.isExpanded()) {
|
if (holder.isExpanded()) {
|
||||||
holder.collapse();
|
holder.collapse();
|
||||||
itemExpanded.remove(sqlPosition);
|
itemExpanded.remove(realIdx);
|
||||||
} else {
|
} else {
|
||||||
holder.expand();
|
holder.expand();
|
||||||
itemExpanded.add(sqlPosition);
|
itemExpanded.add(realIdx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
holder.appName.setText(entry.appName);
|
holder.appName.setText(entry.appName);
|
||||||
@@ -107,10 +102,7 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void notifyDBChanged() {
|
public void notifyDBChanged() {
|
||||||
if (suLogCursor != null)
|
logEntries = suDB.getLogs();
|
||||||
suLogCursor.close();
|
|
||||||
suLogCursor = suDB.getLogCursor();
|
|
||||||
logEntryList = suDB.getLogStructure();
|
|
||||||
itemExpanded.clear();
|
itemExpanded.clear();
|
||||||
sectionExpanded.clear();
|
sectionExpanded.clear();
|
||||||
sectionExpanded.add(0);
|
sectionExpanded.add(0);
|
||||||
@@ -124,7 +116,7 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
|
|||||||
|
|
||||||
SectionHolder(View itemView) {
|
SectionHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
ButterKnife.bind(this, itemView);
|
new SuLogAdapter$SectionHolder_ViewBinding(this, itemView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +134,7 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
|
|||||||
|
|
||||||
LogViewHolder(View itemView) {
|
LogViewHolder(View itemView) {
|
||||||
super(itemView);
|
super(itemView);
|
||||||
ButterKnife.bind(this, itemView);
|
new SuLogAdapter$LogViewHolder_ViewBinding(this, itemView);
|
||||||
container.expandLayout = expandLayout;
|
container.expandLayout = expandLayout;
|
||||||
setupExpandable();
|
setupExpandable();
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
package com.topjohnwu.magisk.adapters;
|
package com.topjohnwu.magisk.adapters;
|
||||||
|
|
||||||
|
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
import android.support.v4.app.FragmentManager;
|
|
||||||
import android.support.v4.app.FragmentPagerAdapter;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import androidx.fragment.app.FragmentManager;
|
||||||
|
import androidx.fragment.app.FragmentPagerAdapter;
|
||||||
|
|
||||||
public class TabFragmentAdapter extends FragmentPagerAdapter {
|
public class TabFragmentAdapter extends FragmentPagerAdapter {
|
||||||
|
|
||||||
private List<Fragment> fragmentList;
|
private List<Fragment> fragmentList;
|
||||||
|
@@ -2,6 +2,7 @@ package com.topjohnwu.magisk.asyncs;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
|
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
@@ -32,7 +33,7 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Void> {
|
|||||||
private void dlSnet() throws Exception {
|
private void dlSnet() throws Exception {
|
||||||
Shell.sh("rm -rf " + dexPath.getParent()).exec();
|
Shell.sh("rm -rf " + dexPath.getParent()).exec();
|
||||||
dexPath.getParentFile().mkdir();
|
dexPath.getParentFile().mkdir();
|
||||||
HttpURLConnection conn = WebService.mustRequest(Data.snetLink, null);
|
HttpURLConnection conn = WebService.mustRequest(Const.Url.SNET_URL);
|
||||||
try (
|
try (
|
||||||
OutputStream out = new BufferedOutputStream(new FileOutputStream(dexPath));
|
OutputStream out = new BufferedOutputStream(new FileOutputStream(dexPath));
|
||||||
InputStream in = new BufferedInputStream(conn.getInputStream())) {
|
InputStream in = new BufferedInputStream(conn.getInputStream())) {
|
||||||
@@ -51,7 +52,7 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Void> {
|
|||||||
.invoke(null, ISafetyNetHelper.class, dexPath.getPath(), getActivity(),
|
.invoke(null, ISafetyNetHelper.class, dexPath.getPath(), getActivity(),
|
||||||
(ISafetyNetHelper.Callback) code ->
|
(ISafetyNetHelper.Callback) code ->
|
||||||
Topic.publish(false, Topic.SNET_CHECK_DONE, code));
|
Topic.publish(false, Topic.SNET_CHECK_DONE, code));
|
||||||
if (helper.getVersion() < Data.snetVersionCode) {
|
if (helper.getVersion() < Const.SNET_EXT_VER) {
|
||||||
throw new Exception();
|
throw new Exception();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,13 +1,13 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
import android.os.AsyncTask;
|
import com.androidnetworking.AndroidNetworking;
|
||||||
|
import com.androidnetworking.error.ANError;
|
||||||
|
import com.androidnetworking.interfaces.JSONObjectRequestListener;
|
||||||
import com.topjohnwu.magisk.BuildConfig;
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.utils.NotificationMgr;
|
import com.topjohnwu.magisk.components.Notifications;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
@@ -42,64 +42,66 @@ public class CheckUpdates {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void fetchUpdates() {
|
public static void check(Runnable cb) {
|
||||||
String jsonStr = "";
|
String url;
|
||||||
switch (Data.updateChannel) {
|
switch (Data.updateChannel) {
|
||||||
case Const.Value.STABLE_CHANNEL:
|
case Const.Value.STABLE_CHANNEL:
|
||||||
jsonStr = WebService.getString(Const.Url.STABLE_URL);
|
url = Const.Url.STABLE_URL;
|
||||||
break;
|
break;
|
||||||
case Const.Value.BETA_CHANNEL:
|
case Const.Value.BETA_CHANNEL:
|
||||||
jsonStr = WebService.getString(Const.Url.BETA_URL);
|
url = Const.Url.BETA_URL;
|
||||||
break;
|
break;
|
||||||
case Const.Value.CUSTOM_CHANNEL:
|
case Const.Value.CUSTOM_CHANNEL:
|
||||||
jsonStr = WebService.getString(Data.MM().prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
|
url = Data.MM().prefs.getString(Const.Key.CUSTOM_CHANNEL, "");
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
AndroidNetworking.get(url).build().getAsJSONObject(new UpdateListener(cb));
|
||||||
JSONObject json;
|
|
||||||
try {
|
|
||||||
json = new JSONObject(jsonStr);
|
|
||||||
} catch (JSONException e) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
JSONObject magisk = getJson(json, "magisk");
|
|
||||||
Data.remoteMagiskVersionString = getString(magisk, "version", null);
|
|
||||||
Data.remoteMagiskVersionCode = getInt(magisk, "versionCode", -1);
|
|
||||||
Data.magiskLink = getString(magisk, "link", null);
|
|
||||||
Data.magiskNoteLink = getString(magisk, "note", null);
|
|
||||||
Data.magiskMD5 = getString(magisk, "md5", null);
|
|
||||||
|
|
||||||
JSONObject manager = getJson(json, "app");
|
|
||||||
Data.remoteManagerVersionString = getString(manager, "version", null);
|
|
||||||
Data.remoteManagerVersionCode = getInt(manager, "versionCode", -1);
|
|
||||||
Data.managerLink = getString(manager, "link", null);
|
|
||||||
Data.managerNoteLink = getString(manager, "note", null);
|
|
||||||
|
|
||||||
JSONObject uninstaller = getJson(json, "uninstaller");
|
|
||||||
Data.uninstallerLink = getString(uninstaller, "link", null);
|
|
||||||
|
|
||||||
JSONObject snet = getJson(json, "snet");
|
|
||||||
Data.snetVersionCode = getInt(snet, "versionCode", -1);
|
|
||||||
Data.snetLink = getString(snet, "link", null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void check(Runnable cb) {
|
|
||||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
|
||||||
fetchUpdates();
|
|
||||||
if (cb != null) {
|
|
||||||
if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) {
|
|
||||||
NotificationMgr.managerUpdate();
|
|
||||||
} else if (Data.magiskVersionCode < Data.remoteMagiskVersionCode) {
|
|
||||||
NotificationMgr.magiskUpdate();
|
|
||||||
}
|
|
||||||
cb.run();
|
|
||||||
}
|
|
||||||
Topic.publish(Topic.UPDATE_CHECK_DONE);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void check() {
|
public static void check() {
|
||||||
check(null);
|
check(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class UpdateListener implements JSONObjectRequestListener {
|
||||||
|
|
||||||
|
private Runnable cb;
|
||||||
|
|
||||||
|
UpdateListener(Runnable callback) {
|
||||||
|
cb = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResponse(JSONObject json) {
|
||||||
|
JSONObject magisk = getJson(json, "magisk");
|
||||||
|
Data.remoteMagiskVersionString = getString(magisk, "version", null);
|
||||||
|
Data.remoteMagiskVersionCode = getInt(magisk, "versionCode", -1);
|
||||||
|
Data.magiskLink = getString(magisk, "link", null);
|
||||||
|
Data.magiskNoteLink = getString(magisk, "note", null);
|
||||||
|
Data.magiskMD5 = getString(magisk, "md5", null);
|
||||||
|
|
||||||
|
JSONObject manager = getJson(json, "app");
|
||||||
|
Data.remoteManagerVersionString = getString(manager, "version", null);
|
||||||
|
Data.remoteManagerVersionCode = getInt(manager, "versionCode", -1);
|
||||||
|
Data.managerLink = getString(manager, "link", null);
|
||||||
|
Data.managerNoteLink = getString(manager, "note", null);
|
||||||
|
|
||||||
|
JSONObject uninstaller = getJson(json, "uninstaller");
|
||||||
|
Data.uninstallerLink = getString(uninstaller, "link", null);
|
||||||
|
|
||||||
|
if (cb != null) {
|
||||||
|
if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) {
|
||||||
|
Notifications.managerUpdate();
|
||||||
|
} else if (Data.magiskVersionCode < Data.remoteMagiskVersionCode) {
|
||||||
|
Notifications.magiskUpdate();
|
||||||
|
}
|
||||||
|
cb.run();
|
||||||
|
}
|
||||||
|
Topic.publish(Topic.UPDATE_CHECK_DONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ANError anError) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,125 @@
|
|||||||
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
|
import com.topjohnwu.magisk.components.ProgressNotification;
|
||||||
|
import com.topjohnwu.magisk.container.Repo;
|
||||||
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FilterInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.util.zip.ZipEntry;
|
||||||
|
import java.util.zip.ZipInputStream;
|
||||||
|
import java.util.zip.ZipOutputStream;
|
||||||
|
|
||||||
|
public class DownloadModule {
|
||||||
|
|
||||||
|
public static void exec(BaseActivity activity, Repo repo, boolean install) {
|
||||||
|
activity.runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
|
||||||
|
() -> AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> dlProcessInstall(repo, install)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void dlProcessInstall(Repo repo, boolean install) {
|
||||||
|
File output = new File(Const.EXTERNAL_PATH, repo.getDownloadFilename());
|
||||||
|
ProgressNotification progress = new ProgressNotification(output.getName());
|
||||||
|
try {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
HttpURLConnection conn = WebService.mustRequest(repo.getZipUrl());
|
||||||
|
ProgressInputStream pis = new ProgressInputStream(conn.getInputStream(),
|
||||||
|
conn.getContentLength(), progress);
|
||||||
|
removeTopFolder(new BufferedInputStream(pis),
|
||||||
|
new BufferedOutputStream(new FileOutputStream(output)));
|
||||||
|
conn.disconnect();
|
||||||
|
if (install) {
|
||||||
|
progress.dismiss();
|
||||||
|
Intent intent = new Intent(mm, Data.classMap.get(FlashActivity.class));
|
||||||
|
intent.setData(Uri.fromFile(output))
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
||||||
|
mm.startActivity(intent);
|
||||||
|
} else {
|
||||||
|
progress.getNotification().setContentTitle(output.getName());
|
||||||
|
progress.dlDone();
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
progress.dlFail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void removeTopFolder(InputStream in, OutputStream out) throws IOException {
|
||||||
|
try (ZipInputStream zin = new ZipInputStream(in);
|
||||||
|
ZipOutputStream zout = new ZipOutputStream(out)) {
|
||||||
|
ZipEntry entry;
|
||||||
|
int off = -1;
|
||||||
|
while ((entry = zin.getNextEntry()) != null) {
|
||||||
|
if (off < 0)
|
||||||
|
off = entry.getName().indexOf('/') + 1;
|
||||||
|
String path = entry.getName().substring(off);
|
||||||
|
if (path.isEmpty())
|
||||||
|
continue;
|
||||||
|
zout.putNextEntry(new ZipEntry(path));
|
||||||
|
if (!entry.isDirectory())
|
||||||
|
ShellUtils.pump(zin, zout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ProgressInputStream extends FilterInputStream {
|
||||||
|
|
||||||
|
private long totalBytes;
|
||||||
|
private long bytesDownloaded;
|
||||||
|
private ProgressNotification progress;
|
||||||
|
|
||||||
|
protected ProgressInputStream(InputStream in, long size, ProgressNotification p) {
|
||||||
|
super(in);
|
||||||
|
totalBytes = size;
|
||||||
|
progress = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateProgress() {
|
||||||
|
progress.onProgress(bytesDownloaded, totalBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
int b = super.read();
|
||||||
|
if (b >= 0) {
|
||||||
|
bytesDownloaded++;
|
||||||
|
updateProgress();
|
||||||
|
}
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b) throws IOException {
|
||||||
|
return read(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
int sz = super.read(b, off, len);
|
||||||
|
if (sz > 0) {
|
||||||
|
bytesDownloaded += sz;
|
||||||
|
updateProgress();
|
||||||
|
}
|
||||||
|
return sz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -4,7 +4,6 @@ import android.app.Activity;
|
|||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
@@ -15,7 +14,6 @@ import com.topjohnwu.magisk.FlashActivity;
|
|||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.container.TarEntry;
|
import com.topjohnwu.magisk.container.TarEntry;
|
||||||
import com.topjohnwu.magisk.utils.Download;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||||
@@ -44,6 +42,8 @@ import java.net.HttpURLConnection;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
||||||
|
|
||||||
private static final int PATCH_MODE = 0;
|
private static final int PATCH_MODE = 0;
|
||||||
@@ -135,7 +135,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
|
|
||||||
if (!ShellUtils.checkSum("MD5", zip, Data.magiskMD5)) {
|
if (!ShellUtils.checkSum("MD5", zip, Data.magiskMD5)) {
|
||||||
console.add("- Downloading zip");
|
console.add("- Downloading zip");
|
||||||
HttpURLConnection conn = WebService.mustRequest(Data.magiskLink, null);
|
HttpURLConnection conn = WebService.mustRequest(Data.magiskLink);
|
||||||
buf = new BufferedInputStream(new ProgressStream(conn), conn.getContentLength());
|
buf = new BufferedInputStream(new ProgressStream(conn), conn.getContentLength());
|
||||||
buf.mark(conn.getContentLength() + 1);
|
buf.mark(conn.getContentLength() + 1);
|
||||||
try (OutputStream out = new FileOutputStream(zip)) {
|
try (OutputStream out = new FileOutputStream(zip)) {
|
||||||
@@ -242,7 +242,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
switch (mode) {
|
switch (mode) {
|
||||||
case PATCH_MODE:
|
case PATCH_MODE:
|
||||||
String fmt = mm.prefs.getString(Const.Key.BOOT_FORMAT, ".img");
|
String fmt = mm.prefs.getString(Const.Key.BOOT_FORMAT, ".img");
|
||||||
File dest = new File(Download.EXTERNAL_PATH, "patched_boot" + fmt);
|
File dest = new File(Const.EXTERNAL_PATH, "patched_boot" + fmt);
|
||||||
dest.getParentFile().mkdirs();
|
dest.getParentFile().mkdirs();
|
||||||
OutputStream out;
|
OutputStream out;
|
||||||
switch (fmt) {
|
switch (fmt) {
|
||||||
|
@@ -1,13 +1,12 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.webkit.WebView;
|
import android.webkit.WebView;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
import org.commonmark.node.Node;
|
import org.commonmark.node.Node;
|
||||||
@@ -18,6 +17,8 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
public class MarkDownWindow extends ParallelTask<Void, Void, String> {
|
public class MarkDownWindow extends ParallelTask<Void, Void, String> {
|
||||||
|
|
||||||
private String mTitle;
|
private String mTitle;
|
||||||
@@ -42,7 +43,7 @@ public class MarkDownWindow extends ParallelTask<Void, Void, String> {
|
|||||||
MagiskManager mm = Data.MM();
|
MagiskManager mm = Data.MM();
|
||||||
String md;
|
String md;
|
||||||
if (mUrl != null) {
|
if (mUrl != null) {
|
||||||
md = WebService.getString(mUrl);
|
md = Utils.dlString(mUrl);
|
||||||
} else {
|
} else {
|
||||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||||
ShellUtils.pump(is, out);
|
ShellUtils.pump(is, out);
|
||||||
|
@@ -1,41 +1,53 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.components.Notifications;
|
||||||
import com.topjohnwu.magisk.utils.RootUtils;
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
|
||||||
import com.topjohnwu.superuser.io.SuFileOutputStream;
|
|
||||||
import com.topjohnwu.utils.JarMap;
|
import com.topjohnwu.utils.JarMap;
|
||||||
import com.topjohnwu.utils.SignAPK;
|
import com.topjohnwu.utils.SignAPK;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.ByteOrder;
|
||||||
|
import java.nio.CharBuffer;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
|
||||||
public class PatchAPK {
|
public class PatchAPK {
|
||||||
|
|
||||||
|
public static final String LOWERALPHA = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
public static final String UPPERALPHA = LOWERALPHA.toUpperCase();
|
||||||
|
public static final String ALPHA = LOWERALPHA + UPPERALPHA;
|
||||||
|
public static final String DIGITS = "0123456789";
|
||||||
|
public static final String ALPHANUM = ALPHA + DIGITS;
|
||||||
|
public static final String ALPHANUMDOTS = ALPHANUM + "............";
|
||||||
|
|
||||||
private static String genPackageName(String prefix, int length) {
|
private static String genPackageName(String prefix, int length) {
|
||||||
StringBuilder builder = new StringBuilder(length);
|
StringBuilder builder = new StringBuilder(length);
|
||||||
builder.append(prefix);
|
builder.append(prefix);
|
||||||
length -= prefix.length();
|
length -= prefix.length();
|
||||||
SecureRandom random = new SecureRandom();
|
SecureRandom random = new SecureRandom();
|
||||||
String base = "abcdefghijklmnopqrstuvwxyz";
|
char next, prev = '.';
|
||||||
String alpha = base + base.toUpperCase();
|
|
||||||
String full = alpha + "0123456789..........";
|
|
||||||
char next, prev = '\0';
|
|
||||||
for (int i = 0; i < length; ++i) {
|
for (int i = 0; i < length; ++i) {
|
||||||
if (prev == '.' || i == length - 1 || i == 0) {
|
if (prev == '.' || i == length - 1) {
|
||||||
next = alpha.charAt(random.nextInt(alpha.length()));
|
next = ALPHA.charAt(random.nextInt(ALPHA.length()));
|
||||||
} else {
|
} else {
|
||||||
next = full.charAt(random.nextInt(full.length()));
|
next = ALPHANUMDOTS.charAt(random.nextInt(ALPHANUMDOTS.length()));
|
||||||
}
|
}
|
||||||
builder.append(next);
|
builder.append(next);
|
||||||
prev = next;
|
prev = next;
|
||||||
@@ -43,88 +55,81 @@ public class PatchAPK {
|
|||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int findOffset(byte buf[], byte pattern[]) {
|
private static boolean findAndPatch(byte xml[], String a, String b) {
|
||||||
|
if (a.length() != b.length())
|
||||||
|
return false;
|
||||||
|
char[] from = a.toCharArray(), to = b.toCharArray();
|
||||||
|
CharBuffer buf = ByteBuffer.wrap(xml).order(ByteOrder.LITTLE_ENDIAN).asCharBuffer();
|
||||||
int offset = -1;
|
int offset = -1;
|
||||||
for (int i = 0; i < buf.length - pattern.length; ++i) {
|
for (int i = 0; i < buf.length() - from.length; ++i) {
|
||||||
boolean match = true;
|
boolean match = true;
|
||||||
for (int j = 0; j < pattern.length; ++j) {
|
for (int j = 0; j < from.length; ++j) {
|
||||||
if (buf[i + j] != pattern[j]) {
|
if (buf.get(i + j) != from[j]) {
|
||||||
match = false;
|
match = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (match) {
|
// Make sure it is null terminated
|
||||||
|
if (match && buf.get(i + from.length) == '\0') {
|
||||||
offset = i;
|
offset = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* It seems that AAPT sometimes generate another type of string format */
|
|
||||||
private static boolean fallbackPatch(byte xml[], String from, String to) {
|
|
||||||
|
|
||||||
byte[] target = new byte[from.length() * 2 + 2];
|
|
||||||
for (int i = 0; i < from.length(); ++i) {
|
|
||||||
target[i * 2] = (byte) from.charAt(i);
|
|
||||||
}
|
|
||||||
int offset = findOffset(xml, target);
|
|
||||||
if (offset < 0)
|
if (offset < 0)
|
||||||
return false;
|
return false;
|
||||||
byte[] dest = new byte[target.length - 2];
|
buf.position(offset);
|
||||||
for (int i = 0; i < to.length(); ++i) {
|
buf.put(to);
|
||||||
dest[i * 2] = (byte) to.charAt(i);
|
|
||||||
}
|
|
||||||
System.arraycopy(dest, 0, xml, offset, dest.length);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean findAndPatch(byte xml[], String from, String to) {
|
private static boolean findAndPatch(byte xml[], int a, int b) {
|
||||||
byte target[] = (from + '\0').getBytes();
|
IntBuffer buf = ByteBuffer.wrap(xml).order(ByteOrder.LITTLE_ENDIAN).asIntBuffer();
|
||||||
int offset = findOffset(xml, target);
|
int len = xml.length / 4;
|
||||||
if (offset < 0)
|
for (int i = 0; i < len; ++i) {
|
||||||
return fallbackPatch(xml, from, to);
|
if (buf.get(i) == a) {
|
||||||
System.arraycopy(to.getBytes(), 0, xml, offset, to.length());
|
buf.put(i, b);
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean patchAndHide() {
|
private static boolean patchAndHide() {
|
||||||
MagiskManager mm = Data.MM();
|
MagiskManager mm = Data.MM();
|
||||||
|
|
||||||
// Generate a new app with random package name
|
// Generate a new app with random package name
|
||||||
SuFile repack = new SuFile("/data/local/tmp/repack.apk");
|
File repack = new File(mm.getFilesDir(), "patched.apk");
|
||||||
String pkg = genPackageName("com.", Const.ORIG_PKG_NAME.length());
|
String pkg = genPackageName("com.", BuildConfig.APPLICATION_ID.length());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
JarMap apk = new JarMap(mm.getPackageCodePath());
|
JarMap apk = new JarMap(mm.getPackageCodePath());
|
||||||
if (!patchPackageID(apk, Const.ORIG_PKG_NAME, pkg))
|
if (!patch(apk, pkg))
|
||||||
return false;
|
return false;
|
||||||
SignAPK.sign(apk, new SuFileOutputStream(repack));
|
SignAPK.sign(apk, new BufferedOutputStream(new FileOutputStream(repack)));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install the application
|
// Install the application
|
||||||
|
repack.setReadable(true, false);
|
||||||
if (!ShellUtils.fastCmdResult("pm install " + repack))
|
if (!ShellUtils.fastCmdResult("pm install " + repack))
|
||||||
return false;
|
return false;;
|
||||||
|
|
||||||
repack.delete();
|
|
||||||
|
|
||||||
mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
|
mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
|
||||||
Data.exportPrefs();
|
Data.exportPrefs();
|
||||||
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME);
|
RootUtils.rmAndLaunch(BuildConfig.APPLICATION_ID, pkg);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean patchPackageID(JarMap apk, String from, String to) {
|
public static boolean patch(JarMap apk, String pkg) {
|
||||||
try {
|
try {
|
||||||
JarEntry je = apk.getJarEntry(Const.ANDROID_MANIFEST);
|
JarEntry je = apk.getJarEntry(Const.ANDROID_MANIFEST);
|
||||||
byte xml[] = apk.getRawData(je);
|
byte xml[] = apk.getRawData(je);
|
||||||
|
|
||||||
if (!findAndPatch(xml, from, to))
|
if (!findAndPatch(xml, BuildConfig.APPLICATION_ID, pkg) ||
|
||||||
return false;
|
!findAndPatch(xml, BuildConfig.APPLICATION_ID + ".provider", pkg + ".provider") ||
|
||||||
if (!findAndPatch(xml, from + ".provider", to + ".provider"))
|
!findAndPatch(xml, R.string.app_name, R.string.re_app_name))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Write in changes
|
// Write in changes
|
||||||
@@ -136,18 +141,16 @@ public class PatchAPK {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void hideManager(Activity activity) {
|
public static void hideManager() {
|
||||||
ProgressDialog dialog = ProgressDialog.show(activity,
|
|
||||||
activity.getString(R.string.hide_manager_toast),
|
|
||||||
activity.getString(R.string.hide_manager_toast2));
|
|
||||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
NotificationCompat.Builder progress =
|
||||||
|
Notifications.progress(mm.getString(R.string.hide_manager_title));
|
||||||
|
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
|
||||||
|
mgr.notify(Const.ID.HIDE_MANAGER_NOTIFICATION_ID, progress.build());
|
||||||
boolean b = patchAndHide();
|
boolean b = patchAndHide();
|
||||||
Data.mainHandler.post(() -> {
|
mgr.cancel(Const.ID.HIDE_MANAGER_NOTIFICATION_ID);
|
||||||
dialog.cancel();
|
if (!b) Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
|
||||||
if (!b) {
|
|
||||||
Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,196 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
|
||||||
import com.topjohnwu.magisk.Data;
|
|
||||||
import com.topjohnwu.magisk.FlashActivity;
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.magisk.components.BaseActivity;
|
|
||||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
|
||||||
import com.topjohnwu.magisk.container.Repo;
|
|
||||||
import com.topjohnwu.magisk.utils.Download;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
|
||||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.FilterInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.net.HttpURLConnection;
|
|
||||||
import java.util.jar.JarEntry;
|
|
||||||
import java.util.jar.JarInputStream;
|
|
||||||
import java.util.jar.JarOutputStream;
|
|
||||||
|
|
||||||
public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|
||||||
|
|
||||||
private ProgressDialog progressDialog;
|
|
||||||
private boolean mInstall;
|
|
||||||
private File mFile;
|
|
||||||
private Repo mRepo;
|
|
||||||
private int progress = 0, total = -1;
|
|
||||||
|
|
||||||
public ProcessRepoZip(BaseActivity context, Repo repo, boolean install) {
|
|
||||||
super(context);
|
|
||||||
mRepo = repo;
|
|
||||||
mInstall = install && Shell.rootAccess();
|
|
||||||
mFile = new File(Download.EXTERNAL_PATH, repo.getDownloadFilename());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void removeTopFolder(File input, File output) throws IOException {
|
|
||||||
JarEntry entry;
|
|
||||||
try (
|
|
||||||
JarInputStream in = new JarInputStream(new BufferedInputStream(new FileInputStream(input)));
|
|
||||||
JarOutputStream out = new JarOutputStream(new BufferedOutputStream(new FileOutputStream(output)))
|
|
||||||
) {
|
|
||||||
String path;
|
|
||||||
while ((entry = in.getNextJarEntry()) != null) {
|
|
||||||
// Remove the top directory from the path
|
|
||||||
path = entry.getName().substring(entry.getName().indexOf("/") + 1);
|
|
||||||
// If it's the top folder, ignore it
|
|
||||||
if (path.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Don't include placeholder
|
|
||||||
if (path.equals("system/placeholder")) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
out.putNextEntry(new JarEntry(path));
|
|
||||||
ShellUtils.pump(in, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected BaseActivity getActivity() {
|
|
||||||
return (BaseActivity) super.getActivity();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
BaseActivity activity = getActivity();
|
|
||||||
mFile.getParentFile().mkdirs();
|
|
||||||
progressDialog = ProgressDialog.show(activity, activity.getString(R.string.zip_download_title), activity.getString(R.string.zip_download_msg, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Boolean doInBackground(Void... params) {
|
|
||||||
BaseActivity activity = getActivity();
|
|
||||||
if (activity == null) return null;
|
|
||||||
try {
|
|
||||||
// Request zip from Internet
|
|
||||||
HttpURLConnection conn = WebService.mustRequest(mRepo.getZipUrl(), null);
|
|
||||||
total = conn.getContentLength();
|
|
||||||
|
|
||||||
// Temp files
|
|
||||||
File temp1 = new File(activity.getCacheDir(), "1.zip");
|
|
||||||
File temp2 = new File(temp1.getParentFile(), "2.zip");
|
|
||||||
temp1.getParentFile().mkdir();
|
|
||||||
|
|
||||||
// First download the zip, Web -> temp1
|
|
||||||
try (
|
|
||||||
InputStream in = new BufferedInputStream(new ProgressInputStream(conn.getInputStream()));
|
|
||||||
OutputStream out = new BufferedOutputStream(new FileOutputStream(temp1))
|
|
||||||
) {
|
|
||||||
ShellUtils.pump(in, out);
|
|
||||||
in.close();
|
|
||||||
}
|
|
||||||
conn.disconnect();
|
|
||||||
|
|
||||||
Data.mainHandler.post(() -> {
|
|
||||||
progressDialog.setTitle(R.string.zip_process_title);
|
|
||||||
progressDialog.setMessage(getActivity().getString(R.string.zip_process_msg));
|
|
||||||
});
|
|
||||||
|
|
||||||
// First remove top folder in Github source zip, temp1 -> temp2
|
|
||||||
removeTopFolder(temp1, temp2);
|
|
||||||
|
|
||||||
// Then sign the zip
|
|
||||||
ZipUtils.signZip(temp2, mFile);
|
|
||||||
|
|
||||||
// Delete temp files
|
|
||||||
temp1.delete();
|
|
||||||
temp2.delete();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean result) {
|
|
||||||
BaseActivity activity = getActivity();
|
|
||||||
if (activity == null) return;
|
|
||||||
progressDialog.dismiss();
|
|
||||||
if (result) {
|
|
||||||
Uri uri = Uri.fromFile(mFile);
|
|
||||||
if (mInstall) {
|
|
||||||
Intent intent = new Intent(activity, FlashActivity.class);
|
|
||||||
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
} else {
|
|
||||||
SnackbarMaker.showUri(activity, uri);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Utils.toast(R.string.process_error, Toast.LENGTH_LONG);
|
|
||||||
}
|
|
||||||
super.onPostExecute(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exec(Void... voids) {
|
|
||||||
getActivity().runWithPermission(
|
|
||||||
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, super::exec);
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ProgressInputStream extends FilterInputStream {
|
|
||||||
|
|
||||||
ProgressInputStream(InputStream in) {
|
|
||||||
super(in);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateDlProgress(int step) {
|
|
||||||
progress += step;
|
|
||||||
progressDialog.setMessage(getActivity().getString(R.string.zip_download_msg,
|
|
||||||
(int) (100 * (double) progress / total + 0.5)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized int read() throws IOException {
|
|
||||||
int b = super.read();
|
|
||||||
if (b > 0) {
|
|
||||||
Data.mainHandler.post(() -> updateDlProgress(1));
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int read(@NonNull byte[] b) throws IOException {
|
|
||||||
return read(b, 0, b.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public synchronized int read(@NonNull byte[] b, int off, int len) throws IOException {
|
|
||||||
int read = super.read(b, off, len);
|
|
||||||
if (read > 0) {
|
|
||||||
Data.mainHandler.post(() -> updateDlProgress(read));
|
|
||||||
}
|
|
||||||
return read;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -3,14 +3,15 @@ package com.topjohnwu.magisk.asyncs;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.androidnetworking.AndroidNetworking;
|
||||||
|
import com.androidnetworking.common.ANRequest;
|
||||||
|
import com.androidnetworking.common.ANResponse;
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.container.Repo;
|
import com.topjohnwu.magisk.container.Repo;
|
||||||
import com.topjohnwu.magisk.utils.Logger;
|
import com.topjohnwu.magisk.utils.Logger;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
|
||||||
|
|
||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
@@ -22,9 +23,7 @@ import java.text.ParseException;
|
|||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
@@ -32,51 +31,45 @@ import java.util.concurrent.Executors;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class UpdateRepos {
|
public class UpdateRepos {
|
||||||
|
|
||||||
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
|
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
|
||||||
private static final int CORE_POOL_SIZE = Math.max(2, CPU_COUNT - 1);
|
private static final int CORE_POOL_SIZE = Math.max(2, CPU_COUNT - 1);
|
||||||
private static final DateFormat dateFormat;
|
private static final DateFormat dateFormat;
|
||||||
|
|
||||||
|
private MagiskManager mm;
|
||||||
|
private Set<String> cached;
|
||||||
|
private ExecutorService threadPool;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||||
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private MagiskManager mm;
|
|
||||||
private Set<String> cached;
|
|
||||||
private ExecutorService threadPool;
|
|
||||||
|
|
||||||
public UpdateRepos() {
|
public UpdateRepos() {
|
||||||
mm = Data.MM();
|
mm = Data.MM();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitTasks() {
|
private void waitTasks() {
|
||||||
threadPool.shutdown();
|
threadPool.shutdown();
|
||||||
try {
|
while (true) {
|
||||||
threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
|
try {
|
||||||
} catch (InterruptedException ignored) {}
|
if (threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS))
|
||||||
|
break;
|
||||||
|
} catch (InterruptedException ignored) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean loadJSON(String jsonString) throws JSONException, ParseException {
|
private void loadJSON(JSONArray array) throws JSONException, ParseException {
|
||||||
JSONArray jsonArray = new JSONArray(jsonString);
|
for (int i = 0; i < array.length(); i++) {
|
||||||
|
JSONObject rawRepo = array.getJSONObject(i);
|
||||||
// Empty page, halt
|
String id = rawRepo.getString("name");
|
||||||
if (jsonArray.length() == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
for (int i = 0; i < jsonArray.length(); i++) {
|
|
||||||
JSONObject rawRepo = jsonArray.getJSONObject(i);
|
|
||||||
String id = rawRepo.getString("description");
|
|
||||||
String name = rawRepo.getString("name");
|
|
||||||
Date date = dateFormat.parse(rawRepo.getString("pushed_at"));
|
Date date = dateFormat.parse(rawRepo.getString("pushed_at"));
|
||||||
Set<String> set = Collections.synchronizedSet(cached);
|
|
||||||
threadPool.execute(() -> {
|
threadPool.execute(() -> {
|
||||||
Repo repo = mm.repoDB.getRepo(id);
|
Repo repo = mm.repoDB.getRepo(id);
|
||||||
try {
|
try {
|
||||||
if (repo == null)
|
if (repo == null)
|
||||||
repo = new Repo(name);
|
repo = new Repo(id);
|
||||||
else
|
else
|
||||||
set.remove(id);
|
cached.remove(id);
|
||||||
repo.update(date);
|
repo.update(date);
|
||||||
mm.repoDB.addRepo(repo);
|
mm.repoDB.addRepo(repo);
|
||||||
} catch (Repo.IllegalRepoException e) {
|
} catch (Repo.IllegalRepoException e) {
|
||||||
@@ -85,42 +78,50 @@ public class UpdateRepos {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We sort repos by last push, which means that we only need to check whether the
|
/* We sort repos by last push, which means that we only need to check whether the
|
||||||
* first page is updated to determine whether the online repo database is changed
|
* first page is updated to determine whether the online repo database is changed
|
||||||
*/
|
*/
|
||||||
private boolean loadPage(int page) {
|
private boolean loadPage(int page) {
|
||||||
Map<String, String> header = new HashMap<>();
|
ANRequest.GetRequestBuilder req = AndroidNetworking.get(Const.Url.REPO_URL)
|
||||||
if (page == 0)
|
.addQueryParameter("page", String.valueOf(page + 1));
|
||||||
header.put(Const.Key.IF_NONE_MATCH, mm.prefs.getString(Const.Key.ETAG_KEY, ""));
|
if (page == 0) {
|
||||||
String url = Utils.fmt(Const.Url.REPO_URL, page + 1);
|
String etag = mm.prefs.getString(Const.Key.ETAG_KEY, null);
|
||||||
|
if (etag != null)
|
||||||
|
req.addHeaders(Const.Key.IF_NONE_MATCH, etag);
|
||||||
|
}
|
||||||
|
ANResponse<JSONArray> res = req.build().executeForJSONArray();
|
||||||
|
if (res.getOkHttpResponse().code() == HttpURLConnection.HTTP_NOT_MODIFIED)
|
||||||
|
return false;
|
||||||
|
// Current page is the last page
|
||||||
|
if (res.getResult() == null || res.getResult().length() == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
HttpURLConnection conn = WebService.request(url, header);
|
loadJSON(res.getResult());
|
||||||
// No updates
|
} catch (JSONException | ParseException e) {
|
||||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED)
|
|
||||||
return false;
|
|
||||||
// Current page is the last page
|
|
||||||
if (!loadJSON(WebService.getString(conn)))
|
|
||||||
return true;
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Should not happen, but if exception occurs, page load fails
|
// Should not happen, but if exception occurs, page load fails
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update ETAG
|
// Update ETAG
|
||||||
if (page == 0) {
|
if (page == 0) {
|
||||||
String etag = header.get(Const.Key.ETAG_KEY);
|
String etag = res.getOkHttpResponse().header(Const.Key.ETAG_KEY);
|
||||||
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
if (etag != null) {
|
||||||
mm.prefs.edit().putString(Const.Key.ETAG_KEY, etag).apply();
|
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
||||||
|
mm.prefs.edit().putString(Const.Key.ETAG_KEY, etag).apply();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String links = header.get(Const.Key.LINK_KEY);
|
String links = res.getOkHttpResponse().header(Const.Key.LINK_KEY);
|
||||||
return links == null || !links.contains("next") || loadPage(page + 1);
|
return links == null || !links.contains("next") || loadPage(page + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean loadPages() {
|
||||||
|
return loadPage(0);
|
||||||
|
}
|
||||||
|
|
||||||
private void fullReload() {
|
private void fullReload() {
|
||||||
Cursor c = mm.repoDB.getRawCursor();
|
Cursor c = mm.repoDB.getRawCursor();
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
@@ -141,10 +142,10 @@ public class UpdateRepos {
|
|||||||
public void exec(boolean force) {
|
public void exec(boolean force) {
|
||||||
Topic.reset(Topic.REPO_LOAD_DONE);
|
Topic.reset(Topic.REPO_LOAD_DONE);
|
||||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
cached = mm.repoDB.getRepoIDSet();
|
cached = Collections.synchronizedSet(mm.repoDB.getRepoIDSet());
|
||||||
threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE);
|
threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE);
|
||||||
|
|
||||||
if (loadPage(0)) {
|
if (loadPages()) {
|
||||||
waitTasks();
|
waitTasks();
|
||||||
// The leftover cached means they are removed from online repo
|
// The leftover cached means they are removed from online repo
|
||||||
mm.repoDB.removeRepo(cached);
|
mm.repoDB.removeRepo(cached);
|
||||||
|
@@ -29,7 +29,6 @@ import android.widget.TextView;
|
|||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
|
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author dvdandroid
|
* @author dvdandroid
|
||||||
@@ -52,7 +51,7 @@ public class AboutCardRow extends LinearLayout {
|
|||||||
public AboutCardRow(Context context, AttributeSet attrs, int defStyleAttr) {
|
public AboutCardRow(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
LayoutInflater.from(context).inflate(R.layout.info_item_row, this);
|
LayoutInflater.from(context).inflate(R.layout.info_item_row, this);
|
||||||
ButterKnife.bind(this, this);
|
new AboutCardRow_ViewBinding(this, this);
|
||||||
|
|
||||||
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AboutCardRow, 0, 0);
|
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AboutCardRow, 0, 0);
|
||||||
String title;
|
String title;
|
||||||
@@ -69,8 +68,6 @@ public class AboutCardRow extends LinearLayout {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOnClickListener(OnClickListener l) {
|
public void setOnClickListener(OnClickListener l) {
|
||||||
super.setOnClickListener(l);
|
|
||||||
|
|
||||||
mView.setOnClickListener(l);
|
mView.setOnClickListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,34 +2,37 @@ package com.topjohnwu.magisk.components;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.StyleRes;
|
|
||||||
import android.support.v7.app.AppCompatActivity;
|
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.NoUIActivity;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.utils.LocaleManager;
|
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
|
||||||
public abstract class FlavorActivity extends AppCompatActivity implements Topic.AutoSubscriber {
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StyleRes;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.core.app.ActivityCompat;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
|
||||||
|
public abstract class BaseActivity extends AppCompatActivity implements Topic.AutoSubscriber {
|
||||||
|
|
||||||
|
public static final String INTENT_PERM = "perm_dialog";
|
||||||
|
|
||||||
|
protected static Runnable permissionGrantCallback;
|
||||||
|
static int[] EMPTY_INT_ARRAY = new int[0];
|
||||||
|
|
||||||
private ActivityResultListener activityResultListener;
|
private ActivityResultListener activityResultListener;
|
||||||
static int[] EMPTY_INT_ARRAY = new int[0];
|
|
||||||
public MagiskManager mm;
|
public MagiskManager mm;
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void attachBaseContext(Context base) {
|
|
||||||
super.attachBaseContext(base);
|
|
||||||
Configuration config = base.getResources().getConfiguration();
|
|
||||||
config.setLocale(LocaleManager.locale);
|
|
||||||
applyOverrideConfiguration(config);
|
|
||||||
mm = Data.MM();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] getSubscribedTopics() {
|
public int[] getSubscribedTopics() {
|
||||||
return EMPTY_INT_ARRAY;
|
return EMPTY_INT_ARRAY;
|
||||||
@@ -40,13 +43,25 @@ public abstract class FlavorActivity extends AppCompatActivity implements Topic.
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void attachBaseContext(Context base) {
|
||||||
|
super.attachBaseContext(base);
|
||||||
|
Configuration config = base.getResources().getConfiguration();
|
||||||
|
config.setLocale(LocaleManager.locale);
|
||||||
|
applyOverrideConfiguration(config);
|
||||||
|
mm = Data.MM();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
Topic.subscribe(this);
|
Topic.subscribe(this);
|
||||||
if (Data.isDarkTheme && getDarkTheme() != -1) {
|
if (Data.isDarkTheme && getDarkTheme() != -1) {
|
||||||
setTheme(getDarkTheme());
|
setTheme(getDarkTheme());
|
||||||
}
|
}
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
String[] perms = getIntent().getStringArrayExtra(INTENT_PERM);
|
||||||
|
if (perms != null)
|
||||||
|
ActivityCompat.requestPermissions(this, perms, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -69,6 +84,34 @@ public abstract class FlavorActivity extends AppCompatActivity implements Topic.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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!
|
||||||
|
permissionGrantCallback = callback;
|
||||||
|
if (!(context instanceof BaseActivity)) {
|
||||||
|
// Start NoUIActivity to show dialog
|
||||||
|
Intent intent = new Intent(context, Data.classMap.get(NoUIActivity.class));
|
||||||
|
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
intent.putExtra(INTENT_PERM, permissions);
|
||||||
|
context.startActivity(intent);
|
||||||
|
} else {
|
||||||
|
ActivityCompat.requestPermissions((BaseActivity) context, permissions, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runWithPermission(String[] permissions, Runnable callback) {
|
||||||
|
runWithPermission(this, permissions, callback);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
if (activityResultListener != null)
|
if (activityResultListener != null)
|
||||||
@@ -81,6 +124,23 @@ public abstract class FlavorActivity extends AppCompatActivity implements Topic.
|
|||||||
super.startActivityForResult(intent, requestCode);
|
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 (permissionGrantCallback != null) {
|
||||||
|
permissionGrantCallback.run();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Toast.makeText(this, R.string.no_rw_storage, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
permissionGrantCallback = null;
|
||||||
|
}
|
||||||
|
|
||||||
public interface ActivityResultListener {
|
public interface ActivityResultListener {
|
||||||
void onActivityResult(int requestCode, int resultCode, Intent data);
|
void onActivityResult(int requestCode, int resultCode, Intent data);
|
||||||
}
|
}
|
@@ -1,15 +1,18 @@
|
|||||||
package com.topjohnwu.magisk.components;
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.support.v4.app.Fragment;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import butterknife.Unbinder;
|
||||||
|
|
||||||
public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
|
public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
|
||||||
|
|
||||||
public MagiskManager mm;
|
public MagiskManager mm;
|
||||||
|
protected Unbinder unbinder = null;
|
||||||
|
|
||||||
public BaseFragment() {
|
public BaseFragment() {
|
||||||
mm = Data.MM();
|
mm = Data.MM();
|
||||||
@@ -27,6 +30,13 @@ public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
|
|||||||
super.onPause();
|
super.onPause();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
if (unbinder != null)
|
||||||
|
unbinder.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startActivityForResult(Intent intent, int requestCode) {
|
public void startActivityForResult(Intent intent, int requestCode) {
|
||||||
startActivityForResult(intent, requestCode, this::onActivityResult);
|
startActivityForResult(intent, requestCode, this::onActivityResult);
|
||||||
@@ -42,6 +52,6 @@ public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] getSubscribedTopics() {
|
public int[] getSubscribedTopics() {
|
||||||
return FlavorActivity.EMPTY_INT_ARRAY;
|
return BaseActivity.EMPTY_INT_ARRAY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -2,11 +2,6 @@ package com.topjohnwu.magisk.components;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.DialogInterface;
|
import android.content.DialogInterface;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.StringRes;
|
|
||||||
import android.support.annotation.StyleRes;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
@@ -15,8 +10,12 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.annotation.StyleRes;
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class CustomAlertDialog extends AlertDialog.Builder {
|
public class CustomAlertDialog extends AlertDialog.Builder {
|
||||||
|
|
||||||
@@ -37,7 +36,7 @@ public class CustomAlertDialog extends AlertDialog.Builder {
|
|||||||
@BindView(R.id.neutral) public Button neutral;
|
@BindView(R.id.neutral) public Button neutral;
|
||||||
|
|
||||||
ViewHolder(View v) {
|
ViewHolder(View v) {
|
||||||
ButterKnife.bind(this, v);
|
new CustomAlertDialog$ViewHolder_ViewBinding(this, v);
|
||||||
messageView.setVisibility(View.GONE);
|
messageView.setVisibility(View.GONE);
|
||||||
negative.setVisibility(View.GONE);
|
negative.setVisibility(View.GONE);
|
||||||
positive.setVisibility(View.GONE);
|
positive.setVisibility(View.GONE);
|
||||||
|
@@ -1,11 +1,12 @@
|
|||||||
package com.topjohnwu.magisk.components;
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.asyncs.InstallMagisk;
|
import com.topjohnwu.magisk.asyncs.InstallMagisk;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public class EnvFixDialog extends CustomAlertDialog {
|
public class EnvFixDialog extends CustomAlertDialog {
|
||||||
|
|
||||||
public EnvFixDialog(@NonNull Activity activity) {
|
public EnvFixDialog(@NonNull Activity activity) {
|
||||||
|
@@ -1,22 +1,24 @@
|
|||||||
package com.topjohnwu.magisk.components;
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.androidnetworking.AndroidNetworking;
|
||||||
|
import com.androidnetworking.error.ANError;
|
||||||
|
import com.androidnetworking.interfaces.DownloadListener;
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.FlashActivity;
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
|
||||||
import com.topjohnwu.magisk.utils.Download;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
class InstallMethodDialog extends AlertDialog.Builder {
|
class InstallMethodDialog extends AlertDialog.Builder {
|
||||||
|
|
||||||
InstallMethodDialog(BaseActivity activity, List<String> options) {
|
InstallMethodDialog(BaseActivity activity, List<String> options) {
|
||||||
@@ -26,54 +28,78 @@ class InstallMethodDialog extends AlertDialog.Builder {
|
|||||||
Intent intent;
|
Intent intent;
|
||||||
switch (idx) {
|
switch (idx) {
|
||||||
case 1:
|
case 1:
|
||||||
if (Data.remoteMagiskVersionCode < 1400) {
|
patchBoot(activity);
|
||||||
SnackbarMaker.make(activity, R.string.no_boot_file_patch_support,
|
|
||||||
Snackbar.LENGTH_LONG).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Utils.toast(R.string.boot_file_patch_msg, Toast.LENGTH_LONG);
|
|
||||||
intent = new Intent(Intent.ACTION_GET_CONTENT).setType("*/*");
|
|
||||||
activity.startActivityForResult(intent, Const.ID.SELECT_BOOT,
|
|
||||||
(requestCode, resultCode, data) -> {
|
|
||||||
if (requestCode == Const.ID.SELECT_BOOT &&
|
|
||||||
resultCode == BaseActivity.RESULT_OK && data != null) {
|
|
||||||
Intent i = new Intent(activity, FlashActivity.class)
|
|
||||||
.putExtra(Const.Key.FLASH_SET_BOOT, data.getData())
|
|
||||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_BOOT);
|
|
||||||
activity.startActivity(i);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
break;
|
break;
|
||||||
case 0:
|
case 0:
|
||||||
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
downloadOnly(activity);
|
||||||
Data.remoteMagiskVersionString, Data.remoteMagiskVersionCode);
|
|
||||||
Download.receive(activity, new DownloadReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onDownloadDone(Context context, Uri uri) {
|
|
||||||
SnackbarMaker.showUri(activity, uri);
|
|
||||||
}
|
|
||||||
}, Data.magiskLink, filename);
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
intent = new Intent(activity, FlashActivity.class)
|
intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
|
||||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
new CustomAlertDialog(activity)
|
installInactiveSlot(activity);
|
||||||
.setTitle(R.string.warning)
|
|
||||||
.setMessage(R.string.install_inactive_slot_msg)
|
|
||||||
.setCancelable(true)
|
|
||||||
.setPositiveButton(R.string.yes, (d, i) -> {
|
|
||||||
Intent it = new Intent(activity, FlashActivity.class)
|
|
||||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_INACTIVE_SLOT);
|
|
||||||
activity.startActivity(it);
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.no_thanks, null)
|
|
||||||
.show();
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void patchBoot(BaseActivity a) {
|
||||||
|
Utils.toast(R.string.boot_file_patch_msg, Toast.LENGTH_LONG);
|
||||||
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT).setType("*/*");
|
||||||
|
a.runWithPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, () ->
|
||||||
|
a.startActivityForResult(intent, Const.ID.SELECT_BOOT,
|
||||||
|
(requestCode, resultCode, data) -> {
|
||||||
|
if (requestCode == Const.ID.SELECT_BOOT &&
|
||||||
|
resultCode == Activity.RESULT_OK && data != null) {
|
||||||
|
Intent i = new Intent(a, Data.classMap.get(FlashActivity.class))
|
||||||
|
.putExtra(Const.Key.FLASH_SET_BOOT, data.getData())
|
||||||
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_BOOT);
|
||||||
|
a.startActivity(i);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void downloadOnly(BaseActivity a) {
|
||||||
|
a.runWithPermission(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, () -> {
|
||||||
|
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
||||||
|
Data.remoteMagiskVersionString, Data.remoteMagiskVersionCode);
|
||||||
|
ProgressNotification progress = new ProgressNotification(filename);
|
||||||
|
AndroidNetworking
|
||||||
|
.download(Data.magiskLink, Const.EXTERNAL_PATH.getPath(), filename)
|
||||||
|
.build()
|
||||||
|
.setDownloadProgressListener(progress)
|
||||||
|
.startDownload(new DownloadListener() {
|
||||||
|
@Override
|
||||||
|
public void onDownloadComplete() {
|
||||||
|
progress.dlDone();
|
||||||
|
SnackbarMaker.make(a,
|
||||||
|
a.getString(R.string.internal_storage, "/Download/" + filename),
|
||||||
|
Snackbar.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ANError anError) {
|
||||||
|
progress.dlFail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void installInactiveSlot(BaseActivity activity) {
|
||||||
|
new CustomAlertDialog(activity)
|
||||||
|
.setTitle(R.string.warning)
|
||||||
|
.setMessage(R.string.install_inactive_slot_msg)
|
||||||
|
.setCancelable(true)
|
||||||
|
.setPositiveButton(R.string.yes, (d, i) -> {
|
||||||
|
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
|
||||||
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_INACTIVE_SLOT);
|
||||||
|
activity.startActivity(intent);
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.no_thanks, null)
|
||||||
|
.show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,36 +1,28 @@
|
|||||||
package com.topjohnwu.magisk.components;
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
||||||
import com.topjohnwu.magisk.receivers.ManagerUpdate;
|
import com.topjohnwu.magisk.utils.DlInstallManager;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public class ManagerInstallDialog extends CustomAlertDialog {
|
public class ManagerInstallDialog extends CustomAlertDialog {
|
||||||
|
|
||||||
public ManagerInstallDialog(@NonNull BaseActivity activity) {
|
public ManagerInstallDialog(@NonNull BaseActivity activity) {
|
||||||
super(activity);
|
super(activity);
|
||||||
MagiskManager mm = Data.MM();
|
MagiskManager mm = Data.MM();
|
||||||
String filename = Utils.fmt("MagiskManager-v%s(%d).apk",
|
String name = Utils.fmt("MagiskManager v%s(%d)",
|
||||||
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
|
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
|
||||||
setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.app_name)));
|
setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.app_name)));
|
||||||
setMessage(mm.getString(R.string.repo_install_msg, filename));
|
setMessage(mm.getString(R.string.repo_install_msg, name));
|
||||||
setCancelable(true);
|
setCancelable(true);
|
||||||
setPositiveButton(R.string.install, (d, i) -> activity.runWithPermission(
|
setPositiveButton(R.string.install, (d, i) -> DlInstallManager.upgrade(name));
|
||||||
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
|
setNegativeButton(R.string.no_thanks, null);
|
||||||
Intent intent = new Intent(mm, ManagerUpdate.class);
|
|
||||||
intent.putExtra(Const.Key.INTENT_SET_LINK, Data.managerLink);
|
|
||||||
intent.putExtra(Const.Key.INTENT_SET_FILENAME, filename);
|
|
||||||
mm.sendBroadcast(intent);
|
|
||||||
}))
|
|
||||||
.setNegativeButton(R.string.no_thanks, null);
|
|
||||||
if (!TextUtils.isEmpty(Data.managerNoteLink)) {
|
if (!TextUtils.isEmpty(Data.managerNoteLink)) {
|
||||||
setNeutralButton(R.string.app_changelog, (d, i) ->
|
setNeutralButton(R.string.app_changelog, (d, i) ->
|
||||||
new MarkDownWindow(activity, null, Data.managerNoteLink).exec());
|
new MarkDownWindow(activity, null, Data.managerNoteLink).exec());
|
||||||
|
@@ -0,0 +1,114 @@
|
|||||||
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
|
import android.app.NotificationChannel;
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.SplashActivity;
|
||||||
|
import com.topjohnwu.magisk.receivers.GeneralReceiver;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
import androidx.core.app.TaskStackBuilder;
|
||||||
|
|
||||||
|
public class Notifications {
|
||||||
|
|
||||||
|
public static void setup(Context c) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
NotificationManager mgr = c.getSystemService(NotificationManager.class);
|
||||||
|
mgr.deleteNotificationChannel("magisk_notification");
|
||||||
|
NotificationChannel channel =
|
||||||
|
new NotificationChannel(Const.ID.UPDATE_NOTIFICATION_CHANNEL,
|
||||||
|
c.getString(R.string.update_channel), NotificationManager.IMPORTANCE_DEFAULT);
|
||||||
|
mgr.createNotificationChannel(channel);
|
||||||
|
channel = new NotificationChannel(Const.ID.PROGRESS_NOTIFICATION_CHANNEL,
|
||||||
|
c.getString(R.string.progress_channel), NotificationManager.IMPORTANCE_LOW);
|
||||||
|
mgr.createNotificationChannel(channel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void magiskUpdate() {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
|
||||||
|
Intent intent = new Intent(mm, Data.classMap.get(SplashActivity.class));
|
||||||
|
intent.putExtra(Const.Key.OPEN_SECTION, "magisk");
|
||||||
|
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mm);
|
||||||
|
stackBuilder.addParentStack(Data.classMap.get(SplashActivity.class));
|
||||||
|
stackBuilder.addNextIntent(intent);
|
||||||
|
PendingIntent pendingIntent = stackBuilder.getPendingIntent(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID,
|
||||||
|
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
|
||||||
|
builder.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
|
.setContentTitle(mm.getString(R.string.magisk_update_title))
|
||||||
|
.setContentText(mm.getString(R.string.magisk_update_available, Data.remoteMagiskVersionString))
|
||||||
|
.setVibrate(new long[]{0, 100, 100, 100})
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setContentIntent(pendingIntent);
|
||||||
|
|
||||||
|
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
|
||||||
|
mgr.notify(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void managerUpdate() {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
String name = Utils.fmt("MagiskManager v%s(%d)",
|
||||||
|
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
|
||||||
|
|
||||||
|
Intent intent = new Intent(mm, Data.classMap.get(GeneralReceiver.class));
|
||||||
|
intent.setAction(Const.Key.BROADCAST_MANAGER_UPDATE);
|
||||||
|
intent.putExtra(Const.Key.INTENT_SET_LINK, Data.managerLink);
|
||||||
|
intent.putExtra(Const.Key.INTENT_SET_NAME, name);
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getBroadcast(mm,
|
||||||
|
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
|
||||||
|
builder.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
|
.setContentTitle(mm.getString(R.string.manager_update_title))
|
||||||
|
.setContentText(mm.getString(R.string.manager_download_install))
|
||||||
|
.setVibrate(new long[]{0, 100, 100, 100})
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setContentIntent(pendingIntent);
|
||||||
|
|
||||||
|
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
|
||||||
|
mgr.notify(Const.ID.APK_UPDATE_NOTIFICATION_ID, builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void dtboPatched() {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
|
||||||
|
Intent intent = new Intent(mm, Data.classMap.get(GeneralReceiver.class))
|
||||||
|
.setAction(Const.Key.BROADCAST_REBOOT);
|
||||||
|
PendingIntent pendingIntent = PendingIntent.getBroadcast(mm,
|
||||||
|
Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||||
|
|
||||||
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.UPDATE_NOTIFICATION_CHANNEL);
|
||||||
|
builder.setSmallIcon(R.drawable.ic_magisk_outline)
|
||||||
|
.setContentTitle(mm.getString(R.string.dtbo_patched_title))
|
||||||
|
.setContentText(mm.getString(R.string.dtbo_patched_reboot))
|
||||||
|
.setVibrate(new long[]{0, 100, 100, 100})
|
||||||
|
.addAction(R.drawable.ic_refresh, mm.getString(R.string.reboot), pendingIntent);
|
||||||
|
|
||||||
|
NotificationManagerCompat mgr = NotificationManagerCompat.from(mm);
|
||||||
|
mgr.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NotificationCompat.Builder progress(String title) {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.PROGRESS_NOTIFICATION_CHANNEL);
|
||||||
|
builder.setPriority(NotificationCompat.PRIORITY_LOW)
|
||||||
|
.setSmallIcon(android.R.drawable.stat_sys_download)
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setProgress(0, 0, true)
|
||||||
|
.setOngoing(true);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,68 @@
|
|||||||
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.androidnetworking.interfaces.DownloadProgressListener;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
|
|
||||||
|
public class ProgressNotification implements DownloadProgressListener {
|
||||||
|
|
||||||
|
private NotificationManagerCompat mgr;
|
||||||
|
private NotificationCompat.Builder builder;
|
||||||
|
private long prevTime;
|
||||||
|
|
||||||
|
public ProgressNotification(String title) {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
mgr = NotificationManagerCompat.from(mm);
|
||||||
|
builder = Notifications.progress(title);
|
||||||
|
prevTime = System.currentTimeMillis();
|
||||||
|
update();
|
||||||
|
Utils.toast(mm.getString(R.string.downloading_toast, title), Toast.LENGTH_SHORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onProgress(long bytesDownloaded, long totalBytes) {
|
||||||
|
long cur = System.currentTimeMillis();
|
||||||
|
if (cur - prevTime >= 1000) {
|
||||||
|
prevTime = cur;
|
||||||
|
int progress = (int) (bytesDownloaded * 100 / totalBytes);
|
||||||
|
builder.setProgress(100, progress, false);
|
||||||
|
builder.setContentText(progress + "%");
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotificationCompat.Builder getNotification() {
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
mgr.notify(hashCode(), builder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dlDone() {
|
||||||
|
builder.setProgress(0, 0, false)
|
||||||
|
.setContentText(Data.MM().getString(R.string.download_complete))
|
||||||
|
.setSmallIcon(R.drawable.ic_check_circle)
|
||||||
|
.setOngoing(false);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dlFail() {
|
||||||
|
builder.setProgress(0, 0, false)
|
||||||
|
.setContentText(Data.MM().getString(R.string.download_file_error))
|
||||||
|
.setSmallIcon(R.drawable.ic_cancel)
|
||||||
|
.setOngoing(false);
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dismiss() {
|
||||||
|
mgr.cancel(hashCode());
|
||||||
|
}
|
||||||
|
}
|
@@ -2,14 +2,15 @@ package com.topjohnwu.magisk.components;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.support.annotation.StringRes;
|
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
public class SnackbarMaker {
|
public class SnackbarMaker {
|
||||||
|
|
||||||
public static Snackbar make(Activity activity, CharSequence text, int duration) {
|
public static Snackbar make(Activity activity, CharSequence text, int duration) {
|
||||||
@@ -34,7 +35,7 @@ public class SnackbarMaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void setup(Snackbar snack) {
|
private static void setup(Snackbar snack) {
|
||||||
TextView text = snack.getView().findViewById(android.support.design.R.id.snackbar_text);
|
TextView text = snack.getView().findViewById(com.google.android.material.R.id.snackbar_text);
|
||||||
text.setMaxLines(Integer.MAX_VALUE);
|
text.setMaxLines(Integer.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,28 +2,29 @@ package com.topjohnwu.magisk.components;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.androidnetworking.AndroidNetworking;
|
||||||
|
import com.androidnetworking.error.ANError;
|
||||||
|
import com.androidnetworking.interfaces.DownloadListener;
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.FlashActivity;
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
|
||||||
import com.topjohnwu.magisk.utils.Download;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public class UninstallDialog extends CustomAlertDialog {
|
public class UninstallDialog extends CustomAlertDialog {
|
||||||
|
|
||||||
public UninstallDialog(@NonNull Activity activity) {
|
public UninstallDialog(@NonNull Activity activity) {
|
||||||
super(activity);
|
super(activity);
|
||||||
MagiskManager mm = Data.MM();
|
|
||||||
setTitle(R.string.uninstall_magisk_title);
|
setTitle(R.string.uninstall_magisk_title);
|
||||||
setMessage(R.string.uninstall_magisk_msg);
|
setMessage(R.string.uninstall_magisk_msg);
|
||||||
setNeutralButton(R.string.restore_img, (d, i) -> {
|
setNeutralButton(R.string.restore_img, (d, i) -> {
|
||||||
@@ -40,17 +41,29 @@ public class UninstallDialog extends CustomAlertDialog {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
if (!TextUtils.isEmpty(Data.uninstallerLink)) {
|
if (!TextUtils.isEmpty(Data.uninstallerLink)) {
|
||||||
setPositiveButton(R.string.complete_uninstall, (d, i) ->
|
setPositiveButton(R.string.complete_uninstall, (d, i) -> {
|
||||||
Download.receive(activity, new DownloadReceiver() {
|
File zip = new File(activity.getFilesDir(), "uninstaller.zip");
|
||||||
|
ProgressNotification progress = new ProgressNotification(zip.getName());
|
||||||
|
AndroidNetworking.download(Data.uninstallerLink, zip.getParent(), zip.getName())
|
||||||
|
.build()
|
||||||
|
.setDownloadProgressListener(progress)
|
||||||
|
.startDownload(new DownloadListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onDownloadDone(Context context, Uri uri) {
|
public void onDownloadComplete() {
|
||||||
Intent intent = new Intent(context, FlashActivity.class)
|
progress.dismiss();
|
||||||
|
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
.setData(uri)
|
.setData(Uri.fromFile(zip))
|
||||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL);
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL);
|
||||||
context.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
}
|
}
|
||||||
}, Data.uninstallerLink, "magisk-uninstaller.zip"));
|
|
||||||
|
@Override
|
||||||
|
public void onError(ANError anError) {
|
||||||
|
progress.dlFail();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,29 +1,35 @@
|
|||||||
package com.topjohnwu.magisk.container;
|
package com.topjohnwu.magisk.container;
|
||||||
|
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public abstract class BaseModule implements Comparable<BaseModule> {
|
public abstract class BaseModule implements Comparable<BaseModule> {
|
||||||
|
|
||||||
private String mId = null, mName, mVersion, mAuthor, mDescription;
|
private String mId, mName, mVersion, mAuthor, mDescription;
|
||||||
private int mVersionCode = -1, minMagiskVersion = -1;
|
private int mVersionCode = -1, minMagiskVersion = -1;
|
||||||
|
|
||||||
protected BaseModule() {}
|
protected BaseModule() {
|
||||||
|
mId = mName = mVersion = mAuthor = mDescription = "";
|
||||||
|
}
|
||||||
|
|
||||||
protected BaseModule(Cursor c) {
|
protected BaseModule(Cursor c) {
|
||||||
mId = c.getString(c.getColumnIndex("id"));
|
mId = nonNull(c.getString(c.getColumnIndex("id")));
|
||||||
mName = c.getString(c.getColumnIndex("name"));
|
mName = nonNull(c.getString(c.getColumnIndex("name")));
|
||||||
mVersion = c.getString(c.getColumnIndex("version"));
|
mVersion = nonNull(c.getString(c.getColumnIndex("version")));
|
||||||
mVersionCode = c.getInt(c.getColumnIndex("versionCode"));
|
mVersionCode = c.getInt(c.getColumnIndex("versionCode"));
|
||||||
mAuthor = c.getString(c.getColumnIndex("author"));
|
mAuthor = nonNull(c.getString(c.getColumnIndex("author")));
|
||||||
mDescription = c.getString(c.getColumnIndex("description"));
|
mDescription = nonNull(c.getString(c.getColumnIndex("description")));
|
||||||
minMagiskVersion = c.getInt(c.getColumnIndex("minMagisk"));
|
minMagiskVersion = c.getInt(c.getColumnIndex("minMagisk"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String nonNull(String s) {
|
||||||
|
return s == null ? "" : s;
|
||||||
|
}
|
||||||
|
|
||||||
public ContentValues getContentValues() {
|
public ContentValues getContentValues() {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put("id", mId);
|
values.put("id", mId);
|
||||||
|
@@ -11,19 +11,19 @@ public class Module extends BaseModule {
|
|||||||
public Module(String path) {
|
public Module(String path) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
parseProps(Shell.Sync.su("dos2unix < " + path + "/module.prop"));
|
parseProps(Shell.su("dos2unix < " + path + "/module.prop").exec().getOut());
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException ignored) {}
|
||||||
|
|
||||||
mRemoveFile = new SuFile(path, "remove");
|
mRemoveFile = new SuFile(path, "remove");
|
||||||
mDisableFile = new SuFile(path, "disable");
|
mDisableFile = new SuFile(path, "disable");
|
||||||
mUpdateFile = new SuFile(path, "update");
|
mUpdateFile = new SuFile(path, "update");
|
||||||
|
|
||||||
if (getId() == null) {
|
if (getId().isEmpty()) {
|
||||||
int sep = path.lastIndexOf('/');
|
int sep = path.lastIndexOf('/');
|
||||||
setId(path.substring(sep + 1));
|
setId(path.substring(sep + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getName() == null) {
|
if (getName().isEmpty()) {
|
||||||
setName(getId());
|
setName(getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,13 +33,11 @@ public class Module extends BaseModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void createDisableFile() {
|
public void createDisableFile() {
|
||||||
mEnable = false;
|
mEnable = !mDisableFile.createNewFile();
|
||||||
mDisableFile.createNewFile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeDisableFile() {
|
public void removeDisableFile() {
|
||||||
mEnable = true;
|
mEnable = mDisableFile.delete();
|
||||||
mDisableFile.delete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
@@ -47,13 +45,11 @@ public class Module extends BaseModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void createRemoveFile() {
|
public void createRemoveFile() {
|
||||||
mRemove = true;
|
mRemove = mRemoveFile.createNewFile();
|
||||||
mRemoveFile.createNewFile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deleteRemoveFile() {
|
public void deleteRemoveFile() {
|
||||||
mRemove = false;
|
mRemove = !mRemoveFile.delete();
|
||||||
mRemoveFile.delete();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean willBeRemoved() {
|
public boolean willBeRemoved() {
|
||||||
|
@@ -3,8 +3,10 @@ package com.topjohnwu.magisk.container;
|
|||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.database.Cursor;
|
|
||||||
import android.support.annotation.NonNull;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
|
||||||
public class Policy implements Comparable<Policy>{
|
public class Policy implements Comparable<Policy>{
|
||||||
@@ -25,16 +27,16 @@ public class Policy implements Comparable<Policy>{
|
|||||||
this.uid = uid;
|
this.uid = uid;
|
||||||
packageName = pkgs[0];
|
packageName = pkgs[0];
|
||||||
info = pm.getApplicationInfo(packageName, 0);
|
info = pm.getApplicationInfo(packageName, 0);
|
||||||
appName = info.loadLabel(pm).toString();
|
appName = Utils.getAppLabel(info, pm);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Policy(Cursor c, PackageManager pm) throws PackageManager.NameNotFoundException {
|
public Policy(ContentValues values, PackageManager pm) throws PackageManager.NameNotFoundException {
|
||||||
uid = c.getInt(c.getColumnIndex("uid"));
|
uid = values.getAsInteger("uid");
|
||||||
packageName = c.getString(c.getColumnIndex("package_name"));
|
packageName = values.getAsString("package_name");
|
||||||
policy = c.getInt(c.getColumnIndex("policy"));
|
policy = values.getAsInteger("policy");
|
||||||
until = c.getLong(c.getColumnIndex("until"));
|
until = values.getAsInteger("until");
|
||||||
logging = c.getInt(c.getColumnIndex("logging")) != 0;
|
logging = values.getAsInteger("logging") != 0;
|
||||||
notification = c.getInt(c.getColumnIndex("notification")) != 0;
|
notification = values.getAsInteger("notification") != 0;
|
||||||
info = pm.getApplicationInfo(packageName, 0);
|
info = pm.getApplicationInfo(packageName, 0);
|
||||||
if (info.uid != uid)
|
if (info.uid != uid)
|
||||||
throw new PackageManager.NameNotFoundException();
|
throw new PackageManager.NameNotFoundException();
|
||||||
|
@@ -7,42 +7,36 @@ import com.topjohnwu.magisk.Const;
|
|||||||
import com.topjohnwu.magisk.utils.Download;
|
import com.topjohnwu.magisk.utils.Download;
|
||||||
import com.topjohnwu.magisk.utils.Logger;
|
import com.topjohnwu.magisk.utils.Logger;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
public class Repo extends BaseModule {
|
public class Repo extends BaseModule {
|
||||||
|
|
||||||
private String repoName;
|
|
||||||
private Date mLastUpdate;
|
private Date mLastUpdate;
|
||||||
|
|
||||||
public Repo(String name) {
|
public Repo(String id) {
|
||||||
repoName = name;
|
setId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Repo(Cursor c) {
|
public Repo(Cursor c) {
|
||||||
super(c);
|
super(c);
|
||||||
repoName = c.getString(c.getColumnIndex("repo_name"));
|
|
||||||
mLastUpdate = new Date(c.getLong(c.getColumnIndex("last_update")));
|
mLastUpdate = new Date(c.getLong(c.getColumnIndex("last_update")));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update() throws IllegalRepoException {
|
public void update() throws IllegalRepoException {
|
||||||
String props[] = Utils.dos2unix(WebService.getString(getManifestUrl())).split("\\n");
|
String props[] = Utils.dlString(getPropUrl()).split("\\n");
|
||||||
try {
|
try {
|
||||||
parseProps(props);
|
parseProps(props);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
throw new IllegalRepoException("Repo [" + repoName + "] parse error: " + e.getMessage());
|
throw new IllegalRepoException("Repo [" + getId() + "] parse error: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (getId() == null) {
|
|
||||||
throw new IllegalRepoException("Repo [" + repoName + "] does not contain id");
|
|
||||||
}
|
|
||||||
if (getVersionCode() < 0) {
|
if (getVersionCode() < 0) {
|
||||||
throw new IllegalRepoException("Repo [" + repoName + "] does not contain versionCode");
|
throw new IllegalRepoException("Repo [" + getId() + "] does not contain versionCode");
|
||||||
}
|
}
|
||||||
if (getMinMagiskVersion() < Const.MIN_MODULE_VER()) {
|
if (getMinMagiskVersion() < Const.MIN_MODULE_VER) {
|
||||||
Logger.debug("Repo [" + repoName + "] is outdated");
|
Logger.debug("Repo [" + getId() + "] is outdated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,25 +48,20 @@ public class Repo extends BaseModule {
|
|||||||
@Override
|
@Override
|
||||||
public ContentValues getContentValues() {
|
public ContentValues getContentValues() {
|
||||||
ContentValues values = super.getContentValues();
|
ContentValues values = super.getContentValues();
|
||||||
values.put("repo_name", repoName);
|
|
||||||
values.put("last_update", mLastUpdate.getTime());
|
values.put("last_update", mLastUpdate.getTime());
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getRepoName() {
|
|
||||||
return repoName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getZipUrl() {
|
public String getZipUrl() {
|
||||||
return String.format(Const.Url.ZIP_URL, repoName);
|
return String.format(Const.Url.ZIP_URL, getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getManifestUrl() {
|
public String getPropUrl() {
|
||||||
return String.format(Const.Url.FILE_URL, repoName, "module.prop");
|
return String.format(Const.Url.FILE_URL, getId(), "module.prop");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDetailUrl() {
|
public String getDetailUrl() {
|
||||||
return String.format(Const.Url.FILE_URL, repoName, "README.md");
|
return String.format(Const.Url.FILE_URL, getId(), "README.md");
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getLastUpdateString() {
|
public String getLastUpdateString() {
|
||||||
|
@@ -1,7 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.container;
|
package com.topjohnwu.magisk.container;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.utils.LocaleManager;
|
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||||
|
|
||||||
@@ -20,17 +19,18 @@ public class SuLogEntry {
|
|||||||
fromUid = policy.uid;
|
fromUid = policy.uid;
|
||||||
packageName = policy.packageName;
|
packageName = policy.packageName;
|
||||||
appName = policy.appName;
|
appName = policy.appName;
|
||||||
|
action = policy.policy == Policy.ALLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SuLogEntry(Cursor c) {
|
public SuLogEntry(ContentValues values) {
|
||||||
fromUid = c.getInt(c.getColumnIndex("from_uid"));
|
fromUid = values.getAsInteger("from_uid");
|
||||||
fromPid = c.getInt(c.getColumnIndex("from_pid"));
|
packageName = values.getAsString("package_name");
|
||||||
toUid = c.getInt(c.getColumnIndex("to_uid"));
|
appName = values.getAsString("app_name");
|
||||||
packageName = c.getString(c.getColumnIndex("package_name"));
|
fromPid = values.getAsInteger("from_pid");
|
||||||
appName = c.getString(c.getColumnIndex("app_name"));
|
command = values.getAsString("command");
|
||||||
command = c.getString(c.getColumnIndex("command"));
|
toUid = values.getAsInteger("to_uid");
|
||||||
action = c.getInt(c.getColumnIndex("action")) != 0;
|
action = values.getAsInteger("action") != 0;
|
||||||
date = new Date(c.getLong(c.getColumnIndex("time")));
|
date = new Date(values.getAsLong("time"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContentValues getContentValues() {
|
public ContentValues getContentValues() {
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
package com.topjohnwu.magisk.container;
|
package com.topjohnwu.magisk.container;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -9,6 +7,8 @@ import java.util.HashMap;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public class ValueSortedMap<K, V extends Comparable<? super V>> extends HashMap<K, V> {
|
public class ValueSortedMap<K, V extends Comparable<? super V>> extends HashMap<K, V> {
|
||||||
|
|
||||||
private List<V> sorted = new ArrayList<>();
|
private List<V> sorted = new ArrayList<>();
|
||||||
|
@@ -0,0 +1,66 @@
|
|||||||
|
package com.topjohnwu.magisk.database;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.container.Policy;
|
||||||
|
import com.topjohnwu.magisk.container.SuLogEntry;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class MagiskDB {
|
||||||
|
|
||||||
|
static final String POLICY_TABLE = "policies";
|
||||||
|
static final String LOG_TABLE = "logs";
|
||||||
|
static final String SETTINGS_TABLE = "settings";
|
||||||
|
static final String STRINGS_TABLE = "strings";
|
||||||
|
static final File LEGACY_MANAGER_DB =
|
||||||
|
new File(Utils.fmt("/sbin/.magisk/db-%d/magisk.db", Const.USER_ID));
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public static MagiskDB getInstance() {
|
||||||
|
if (LEGACY_MANAGER_DB.canWrite()) {
|
||||||
|
return MagiskDBLegacy.newInstance();
|
||||||
|
} else if (Shell.rootAccess()) {
|
||||||
|
return Data.magiskVersionCode >= Const.MAGISK_VER.CMDLINE_DB ?
|
||||||
|
new MagiskDBCmdline() : MagiskDBLegacy.newInstance();
|
||||||
|
} else {
|
||||||
|
return new MagiskDB();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearOutdated() {}
|
||||||
|
|
||||||
|
public void deletePolicy(Policy policy) {
|
||||||
|
deletePolicy(policy.uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void deletePolicy(String pkg) {}
|
||||||
|
|
||||||
|
public void deletePolicy(int uid) {}
|
||||||
|
|
||||||
|
public Policy getPolicy(int uid) { return null; }
|
||||||
|
|
||||||
|
public void updatePolicy(Policy policy) {}
|
||||||
|
|
||||||
|
public List<Policy> getPolicyList() { return Collections.emptyList(); }
|
||||||
|
|
||||||
|
public List<List<SuLogEntry>> getLogs() { return Collections.emptyList(); }
|
||||||
|
|
||||||
|
public void addLog(SuLogEntry log) {}
|
||||||
|
|
||||||
|
public void clearLogs() {}
|
||||||
|
|
||||||
|
public void setSettings(String key, int value) {}
|
||||||
|
|
||||||
|
public int getSettings(String key, int defaultValue) { return defaultValue; }
|
||||||
|
|
||||||
|
public void setStrings(String key, String value) {}
|
||||||
|
|
||||||
|
public String getStrings(String key, String defaultValue) { return defaultValue; }
|
||||||
|
}
|
@@ -0,0 +1,189 @@
|
|||||||
|
package com.topjohnwu.magisk.database;
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.container.Policy;
|
||||||
|
import com.topjohnwu.magisk.container.SuLogEntry;
|
||||||
|
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
|
import java.text.DateFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class MagiskDBCmdline extends MagiskDB {
|
||||||
|
|
||||||
|
private PackageManager pm;
|
||||||
|
|
||||||
|
public MagiskDBCmdline() {
|
||||||
|
pm = Data.MM().getPackageManager();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> rawSQL(String fmt, Object... args) {
|
||||||
|
return Shell.su("magisk --sqlite '" + Utils.fmt(fmt, args) + "'").exec().getOut();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ContentValues> SQL(String fmt, Object... args) {
|
||||||
|
List<ContentValues> list = new ArrayList<>();
|
||||||
|
for (String raw : rawSQL(fmt, args)) {
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
String[] cols = raw.split("\\|");
|
||||||
|
for (String col : cols) {
|
||||||
|
String[] pair = col.split("=", 2);
|
||||||
|
if (pair.length != 2)
|
||||||
|
continue;
|
||||||
|
values.put(pair[0], pair[1]);
|
||||||
|
}
|
||||||
|
list.add(values);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toSQL(ContentValues values) {
|
||||||
|
StringBuilder keys = new StringBuilder(), vals = new StringBuilder();
|
||||||
|
keys.append('(');
|
||||||
|
vals.append("VALUES(");
|
||||||
|
boolean first = true;
|
||||||
|
for (Map.Entry<String, Object> entry : values.valueSet()) {
|
||||||
|
if (!first) {
|
||||||
|
keys.append(',');
|
||||||
|
vals.append(',');
|
||||||
|
} else {
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
keys.append(entry.getKey());
|
||||||
|
vals.append('"');
|
||||||
|
vals.append(entry.getValue());
|
||||||
|
vals.append('"');
|
||||||
|
}
|
||||||
|
keys.append(')');
|
||||||
|
vals.append(')');
|
||||||
|
keys.append(vals);
|
||||||
|
return keys.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearOutdated() {
|
||||||
|
rawSQL(
|
||||||
|
"DELETE FROM %s WHERE until > 0 AND until < %d;" +
|
||||||
|
"DELETE FROM %s WHERE time < %d",
|
||||||
|
POLICY_TABLE, System.currentTimeMillis() / 1000,
|
||||||
|
LOG_TABLE, System.currentTimeMillis() - Data.suLogTimeout * 86400000
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deletePolicy(String pkg) {
|
||||||
|
rawSQL("DELETE FROM %s WHERE package_name=\"%s\"", POLICY_TABLE, pkg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deletePolicy(int uid) {
|
||||||
|
rawSQL("DELETE FROM %s WHERE uid=%d", POLICY_TABLE, uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Policy getPolicy(int uid) {
|
||||||
|
List<ContentValues> res =
|
||||||
|
SQL("SELECT * FROM %s WHERE uid=%d", POLICY_TABLE, uid);
|
||||||
|
if (!res.isEmpty()) {
|
||||||
|
try {
|
||||||
|
return new Policy(res.get(0), pm);
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
deletePolicy(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updatePolicy(Policy policy) {
|
||||||
|
rawSQL("REPLACE INTO %s %s", POLICY_TABLE, toSQL(policy.getContentValues()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Policy> getPolicyList() {
|
||||||
|
List<Policy> list = new ArrayList<>();
|
||||||
|
for (ContentValues values : SQL("SELECT * FROM %s WHERE uid/100000=%d", POLICY_TABLE, Const.USER_ID)) {
|
||||||
|
try {
|
||||||
|
list.add(new Policy(values, pm));
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
deletePolicy(values.getAsInteger("uid"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(list);
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<List<SuLogEntry>> getLogs() {
|
||||||
|
List<List<SuLogEntry>> ret = new ArrayList<>();
|
||||||
|
List<SuLogEntry> list = null;
|
||||||
|
String dateString = null, newString;
|
||||||
|
for (ContentValues values : SQL("SELECT * FROM %s ORDER BY time DESC", LOG_TABLE)) {
|
||||||
|
Date date = new Date(values.getAsLong("time"));
|
||||||
|
newString = DateFormat.getDateInstance(DateFormat.MEDIUM, LocaleManager.locale).format(date);
|
||||||
|
if (!TextUtils.equals(dateString, newString)) {
|
||||||
|
dateString = newString;
|
||||||
|
list = new ArrayList<>();
|
||||||
|
ret.add(list);
|
||||||
|
}
|
||||||
|
list.add(new SuLogEntry(values));
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addLog(SuLogEntry log) {
|
||||||
|
rawSQL("INSERT INTO %s %s", LOG_TABLE, toSQL(log.getContentValues()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clearLogs() {
|
||||||
|
rawSQL("DELETE FROM %s", LOG_TABLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSettings(String key, int value) {
|
||||||
|
ContentValues data = new ContentValues();
|
||||||
|
data.put("key", key);
|
||||||
|
data.put("value", value);
|
||||||
|
rawSQL("REPLACE INTO %s %s", SETTINGS_TABLE, toSQL(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getSettings(String key, int defaultValue) {
|
||||||
|
List<ContentValues> res = SQL("SELECT value FROM %s WHERE key=\"%s\"", SETTINGS_TABLE, key);
|
||||||
|
if (res.isEmpty())
|
||||||
|
return defaultValue;
|
||||||
|
return res.get(0).getAsInteger("value");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setStrings(String key, String value) {
|
||||||
|
if (value == null) {
|
||||||
|
rawSQL("DELETE FROM %s WHERE key=\"%s\"", STRINGS_TABLE, key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ContentValues data = new ContentValues();
|
||||||
|
data.put("key", key);
|
||||||
|
data.put("value", value);
|
||||||
|
rawSQL("REPLACE INTO %s %s", STRINGS_TABLE, toSQL(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getStrings(String key, String defaultValue) {
|
||||||
|
List<ContentValues> res = SQL("SELECT value FROM %s WHERE key=\"%s\"", STRINGS_TABLE, key);
|
||||||
|
if (res.isEmpty())
|
||||||
|
return defaultValue;
|
||||||
|
return res.get(0).getAsString("value");
|
||||||
|
}
|
||||||
|
}
|
@@ -4,10 +4,10 @@ import android.content.ContentValues;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
import android.database.DatabaseUtils;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
@@ -22,41 +22,33 @@ import com.topjohnwu.magisk.utils.Utils;
|
|||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class MagiskDatabaseHelper {
|
public class MagiskDBLegacy extends MagiskDB {
|
||||||
|
|
||||||
private static final int DATABASE_VER = 6;
|
private static final int DATABASE_VER = 6;
|
||||||
private static final int OLD_DATABASE_VER = 5;
|
private static final int OLD_DATABASE_VER = 5;
|
||||||
private static final String POLICY_TABLE = "policies";
|
|
||||||
private static final String LOG_TABLE = "logs";
|
|
||||||
private static final String SETTINGS_TABLE = "settings";
|
|
||||||
private static final String STRINGS_TABLE = "strings";
|
|
||||||
|
|
||||||
private PackageManager pm;
|
private PackageManager pm;
|
||||||
private SQLiteDatabase db;
|
private SQLiteDatabase db;
|
||||||
private MagiskManager mm;
|
|
||||||
|
|
||||||
@NonNull
|
static MagiskDBLegacy newInstance() {
|
||||||
public static MagiskDatabaseHelper getInstance(MagiskManager mm) {
|
|
||||||
try {
|
try {
|
||||||
return new MagiskDatabaseHelper(mm);
|
return new MagiskDBLegacy();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Let's cleanup everything and try again
|
// Let's cleanup everything and try again
|
||||||
Shell.su("db_clean '*'").exec();
|
Shell.su("db_clean '*'").exec();
|
||||||
return new MagiskDatabaseHelper(mm);
|
return new MagiskDBLegacy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MagiskDatabaseHelper(MagiskManager context) {
|
private MagiskDBLegacy() {
|
||||||
mm = context;
|
pm = Data.MM().getPackageManager();
|
||||||
pm = mm.getPackageManager();
|
db = openDatabase();
|
||||||
db = openDatabase(mm);
|
|
||||||
db.disableWriteAheadLogging();
|
db.disableWriteAheadLogging();
|
||||||
int version = Data.magiskVersionCode >= Const.MAGISK_VER.DBVER_SIX ? DATABASE_VER : OLD_DATABASE_VER;
|
int version = Data.magiskVersionCode >= Const.MAGISK_VER.DBVER_SIX ? DATABASE_VER : OLD_DATABASE_VER;
|
||||||
int curVersion = db.getVersion();
|
int curVersion = db.getVersion();
|
||||||
@@ -70,48 +62,37 @@ public class MagiskDatabaseHelper {
|
|||||||
clearOutdated();
|
clearOutdated();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SQLiteDatabase openDatabase(MagiskManager mm) {
|
private SQLiteDatabase openDatabase() {
|
||||||
final File DB_FILE = new File(Utils.fmt("/sbin/.core/db-%d/magisk.db", Const.USER_ID));
|
MagiskManager mm = Data.MM();
|
||||||
Context de = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
|
Context de = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
|
||||||
? mm.createDeviceProtectedStorageContext() : mm;
|
? mm.createDeviceProtectedStorageContext() : mm;
|
||||||
if (!DB_FILE.canWrite()) {
|
if (!LEGACY_MANAGER_DB.canWrite()) {
|
||||||
if (!Shell.rootAccess()) {
|
if (!Shell.rootAccess() || Data.magiskVersionCode < 0) {
|
||||||
// We don't want the app to crash, create a db and return
|
// We don't want the app to crash, create a db and return
|
||||||
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
||||||
}
|
}
|
||||||
// Cleanup
|
// Cleanup
|
||||||
Shell.su("db_clean " + Const.USER_ID).exec();
|
Shell.su("db_clean " + Const.USER_ID).exec();
|
||||||
if (Data.magiskVersionCode < Const.MAGISK_VER.FBE_AWARE) {
|
// Global database
|
||||||
// Super old legacy mode
|
final SuFile GLOBAL_DB = new SuFile("/data/adb/magisk.db");
|
||||||
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
mm.deleteDatabase("su.db");
|
||||||
} else if (Data.magiskVersionCode < Const.MAGISK_VER.HIDDEN_PATH) {
|
de.deleteDatabase("su.db");
|
||||||
// Legacy mode with FBE aware
|
if (Data.magiskVersionCode < Const.MAGISK_VER.SEPOL_REFACTOR) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
// We need some additional policies on old versions
|
||||||
de.moveDatabaseFrom(mm, "su.db");
|
Shell.su("db_sepatch").exec();
|
||||||
}
|
}
|
||||||
return de.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
|
if (!GLOBAL_DB.exists()) {
|
||||||
} else {
|
Shell.su("db_init").exec();
|
||||||
// Global database
|
SQLiteDatabase.openOrCreateDatabase(GLOBAL_DB, null).close();
|
||||||
final SuFile GLOBAL_DB = new SuFile("/data/adb/magisk.db");
|
Shell.su("db_restore").exec();
|
||||||
mm.deleteDatabase("su.db");
|
|
||||||
de.deleteDatabase("su.db");
|
|
||||||
if (Data.magiskVersionCode < Const.MAGISK_VER.SEPOL_REFACTOR) {
|
|
||||||
// We need some additional policies on old versions
|
|
||||||
Shell.su("db_sepatch").exec();
|
|
||||||
}
|
|
||||||
if (!GLOBAL_DB.exists()) {
|
|
||||||
Shell.su("db_init").exec();
|
|
||||||
SQLiteDatabase.openOrCreateDatabase(GLOBAL_DB, null).close();
|
|
||||||
Shell.su("db_restore").exec();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Shell.su("db_setup " + Process.myUid()).exec();
|
Shell.su("db_setup " + Process.myUid()).exec();
|
||||||
}
|
}
|
||||||
// Not using legacy mode, open the mounted global DB
|
// Not using legacy mode, open the mounted global DB
|
||||||
return SQLiteDatabase.openOrCreateDatabase(DB_FILE, null);
|
return SQLiteDatabase.openOrCreateDatabase(LEGACY_MANAGER_DB, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion) {
|
private void onUpgrade(SQLiteDatabase db, int oldVersion) {
|
||||||
if (oldVersion == 0) {
|
if (oldVersion == 0) {
|
||||||
createTables(db);
|
createTables(db);
|
||||||
oldVersion = 3;
|
oldVersion = 3;
|
||||||
@@ -146,13 +127,13 @@ public class MagiskDatabaseHelper {
|
|||||||
}
|
}
|
||||||
if (oldVersion == 5) {
|
if (oldVersion == 5) {
|
||||||
setSettings(Const.Key.SU_FINGERPRINT,
|
setSettings(Const.Key.SU_FINGERPRINT,
|
||||||
mm.prefs.getBoolean(Const.Key.SU_FINGERPRINT, false) ? 1 : 0);
|
Data.MM().prefs.getBoolean(Const.Key.SU_FINGERPRINT, false) ? 1 : 0);
|
||||||
++oldVersion;
|
++oldVersion;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove everything, we do not support downgrade
|
// Remove everything, we do not support downgrade
|
||||||
public void onDowngrade(SQLiteDatabase db) {
|
private void onDowngrade(SQLiteDatabase db) {
|
||||||
Utils.toast(R.string.su_db_corrupt, Toast.LENGTH_LONG);
|
Utils.toast(R.string.su_db_corrupt, Toast.LENGTH_LONG);
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + POLICY_TABLE);
|
db.execSQL("DROP TABLE IF EXISTS " + POLICY_TABLE);
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + LOG_TABLE);
|
db.execSQL("DROP TABLE IF EXISTS " + LOG_TABLE);
|
||||||
@@ -165,22 +146,23 @@ public class MagiskDatabaseHelper {
|
|||||||
// Policies
|
// Policies
|
||||||
db.execSQL(
|
db.execSQL(
|
||||||
"CREATE TABLE IF NOT EXISTS " + POLICY_TABLE + " " +
|
"CREATE TABLE IF NOT EXISTS " + POLICY_TABLE + " " +
|
||||||
"(uid INT, package_name TEXT, policy INT, " +
|
"(uid INT, package_name TEXT, policy INT, " +
|
||||||
"until INT, logging INT, notification INT, " +
|
"until INT, logging INT, notification INT, " +
|
||||||
"PRIMARY KEY(uid))");
|
"PRIMARY KEY(uid))");
|
||||||
|
|
||||||
// Logs
|
// Logs
|
||||||
db.execSQL(
|
db.execSQL(
|
||||||
"CREATE TABLE IF NOT EXISTS " + LOG_TABLE + " " +
|
"CREATE TABLE IF NOT EXISTS " + LOG_TABLE + " " +
|
||||||
"(from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, " +
|
"(from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, " +
|
||||||
"to_uid INT, action INT, time INT, command TEXT)");
|
"to_uid INT, action INT, time INT, command TEXT)");
|
||||||
|
|
||||||
// Settings
|
// Settings
|
||||||
db.execSQL(
|
db.execSQL(
|
||||||
"CREATE TABLE IF NOT EXISTS " + SETTINGS_TABLE + " " +
|
"CREATE TABLE IF NOT EXISTS " + SETTINGS_TABLE + " " +
|
||||||
"(key TEXT, value INT, PRIMARY KEY(key))");
|
"(key TEXT, value INT, PRIMARY KEY(key))");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void clearOutdated() {
|
public void clearOutdated() {
|
||||||
// Clear outdated policies
|
// Clear outdated policies
|
||||||
db.delete(POLICY_TABLE, Utils.fmt("until > 0 AND until < %d", System.currentTimeMillis() / 1000), null);
|
db.delete(POLICY_TABLE, Utils.fmt("until > 0 AND until < %d", System.currentTimeMillis() / 1000), null);
|
||||||
@@ -188,23 +170,24 @@ public class MagiskDatabaseHelper {
|
|||||||
db.delete(LOG_TABLE, Utils.fmt("time < %d", System.currentTimeMillis() - Data.suLogTimeout * 86400000), null);
|
db.delete(LOG_TABLE, Utils.fmt("time < %d", System.currentTimeMillis() - Data.suLogTimeout * 86400000), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deletePolicy(Policy policy) {
|
@Override
|
||||||
deletePolicy(policy.uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void deletePolicy(String pkg) {
|
public void deletePolicy(String pkg) {
|
||||||
db.delete(POLICY_TABLE, "package_name=?", new String[] { pkg });
|
db.delete(POLICY_TABLE, "package_name=?", new String[] { pkg });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void deletePolicy(int uid) {
|
public void deletePolicy(int uid) {
|
||||||
db.delete(POLICY_TABLE, Utils.fmt("uid=%d", uid), null);
|
db.delete(POLICY_TABLE, Utils.fmt("uid=%d", uid), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Policy getPolicy(int uid) {
|
public Policy getPolicy(int uid) {
|
||||||
Policy policy = null;
|
Policy policy = null;
|
||||||
try (Cursor c = db.query(POLICY_TABLE, null, Utils.fmt("uid=%d", uid), null, null, null, null)) {
|
try (Cursor c = db.query(POLICY_TABLE, null, Utils.fmt("uid=%d", uid), null, null, null, null)) {
|
||||||
if (c.moveToNext()) {
|
if (c.moveToNext()) {
|
||||||
policy = new Policy(c, pm);
|
ContentValues values = new ContentValues();
|
||||||
|
DatabaseUtils.cursorRowToContentValues(c, values);
|
||||||
|
policy = new Policy(values, pm);
|
||||||
}
|
}
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
deletePolicy(uid);
|
deletePolicy(uid);
|
||||||
@@ -213,21 +196,21 @@ public class MagiskDatabaseHelper {
|
|||||||
return policy;
|
return policy;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addPolicy(Policy policy) {
|
@Override
|
||||||
|
public void updatePolicy(Policy policy) {
|
||||||
db.replace(POLICY_TABLE, null, policy.getContentValues());
|
db.replace(POLICY_TABLE, null, policy.getContentValues());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updatePolicy(Policy policy) {
|
@Override
|
||||||
db.update(POLICY_TABLE, policy.getContentValues(), Utils.fmt("uid=%d", policy.uid), null);
|
public List<Policy> getPolicyList() {
|
||||||
}
|
|
||||||
|
|
||||||
public List<Policy> getPolicyList(PackageManager pm) {
|
|
||||||
try (Cursor c = db.query(POLICY_TABLE, null, Utils.fmt("uid/100000=%d", Const.USER_ID),
|
try (Cursor c = db.query(POLICY_TABLE, null, Utils.fmt("uid/100000=%d", Const.USER_ID),
|
||||||
null, null, null, null)) {
|
null, null, null, null)) {
|
||||||
List<Policy> ret = new ArrayList<>(c.getCount());
|
List<Policy> ret = new ArrayList<>(c.getCount());
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
try {
|
try {
|
||||||
Policy policy = new Policy(c, pm);
|
ContentValues values = new ContentValues();
|
||||||
|
DatabaseUtils.cursorRowToContentValues(c, values);
|
||||||
|
Policy policy = new Policy(values, pm);
|
||||||
ret.add(policy);
|
ret.add(policy);
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
// The app no longer exist, remove from DB
|
// The app no longer exist, remove from DB
|
||||||
@@ -239,11 +222,12 @@ public class MagiskDatabaseHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<List<Integer>> getLogStructure() {
|
@Override
|
||||||
try (Cursor c = db.query(LOG_TABLE, new String[] { "time" }, Utils.fmt("from_uid/100000=%d", Const.USER_ID),
|
public List<List<SuLogEntry>> getLogs() {
|
||||||
|
try (Cursor c = db.query(LOG_TABLE, null, Utils.fmt("from_uid/100000=%d", Const.USER_ID),
|
||||||
null, null, null, "time DESC")) {
|
null, null, null, "time DESC")) {
|
||||||
List<List<Integer>> ret = new ArrayList<>();
|
List<List<SuLogEntry>> ret = new ArrayList<>();
|
||||||
List<Integer> list = null;
|
List<SuLogEntry> list = null;
|
||||||
String dateString = null, newString;
|
String dateString = null, newString;
|
||||||
while (c.moveToNext()) {
|
while (c.moveToNext()) {
|
||||||
Date date = new Date(c.getLong(c.getColumnIndex("time")));
|
Date date = new Date(c.getLong(c.getColumnIndex("time")));
|
||||||
@@ -253,25 +237,25 @@ public class MagiskDatabaseHelper {
|
|||||||
list = new ArrayList<>();
|
list = new ArrayList<>();
|
||||||
ret.add(list);
|
ret.add(list);
|
||||||
}
|
}
|
||||||
list.add(c.getPosition());
|
ContentValues values = new ContentValues();
|
||||||
|
DatabaseUtils.cursorRowToContentValues(c, values);
|
||||||
|
list.add(new SuLogEntry(values));
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cursor getLogCursor() {
|
@Override
|
||||||
return db.query(LOG_TABLE, null, Utils.fmt("from_uid/100000=%d", Const.USER_ID),
|
|
||||||
null, null, null, "time DESC");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addLog(SuLogEntry log) {
|
public void addLog(SuLogEntry log) {
|
||||||
db.insert(LOG_TABLE, null, log.getContentValues());
|
db.insert(LOG_TABLE, null, log.getContentValues());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void clearLogs() {
|
public void clearLogs() {
|
||||||
db.delete(LOG_TABLE, null, null);
|
db.delete(LOG_TABLE, null, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setSettings(String key, int value) {
|
public void setSettings(String key, int value) {
|
||||||
ContentValues data = new ContentValues();
|
ContentValues data = new ContentValues();
|
||||||
data.put("key", key);
|
data.put("key", key);
|
||||||
@@ -279,6 +263,7 @@ public class MagiskDatabaseHelper {
|
|||||||
db.replace(SETTINGS_TABLE, null, data);
|
db.replace(SETTINGS_TABLE, null, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int getSettings(String key, int defaultValue) {
|
public int getSettings(String key, int defaultValue) {
|
||||||
int value = defaultValue;
|
int value = defaultValue;
|
||||||
try (Cursor c = db.query(SETTINGS_TABLE, null, "key=?",new String[] { key }, null, null, null)) {
|
try (Cursor c = db.query(SETTINGS_TABLE, null, "key=?",new String[] { key }, null, null, null)) {
|
||||||
@@ -289,6 +274,7 @@ public class MagiskDatabaseHelper {
|
|||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void setStrings(String key, String value) {
|
public void setStrings(String key, String value) {
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
db.delete(STRINGS_TABLE, "key=?", new String[] { key });
|
db.delete(STRINGS_TABLE, "key=?", new String[] { key });
|
||||||
@@ -300,6 +286,7 @@ public class MagiskDatabaseHelper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getStrings(String key, String defaultValue) {
|
public String getStrings(String key, String defaultValue) {
|
||||||
String value = defaultValue;
|
String value = defaultValue;
|
||||||
try (Cursor c = db.query(STRINGS_TABLE, null, "key=?",new String[] { key }, null, null, null)) {
|
try (Cursor c = db.query(STRINGS_TABLE, null, "key=?",new String[] { key }, null, null, null)) {
|
@@ -16,7 +16,7 @@ import java.util.Set;
|
|||||||
|
|
||||||
public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
||||||
|
|
||||||
private static final int DATABASE_VER = 3;
|
private static final int DATABASE_VER = 4;
|
||||||
private static final String TABLE_NAME = "repos";
|
private static final String TABLE_NAME = "repos";
|
||||||
|
|
||||||
private SQLiteDatabase mDb;
|
private SQLiteDatabase mDb;
|
||||||
@@ -29,8 +29,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
mDb = getWritableDatabase();
|
mDb = getWritableDatabase();
|
||||||
|
|
||||||
// Remove outdated repos
|
// Remove outdated repos
|
||||||
mDb.delete(TABLE_NAME, "minMagisk<?",
|
mDb.delete(TABLE_NAME, "minMagisk<?", new String[] { String.valueOf(Const.MIN_MODULE_VER) });
|
||||||
new String[] { String.valueOf(Const.MIN_MODULE_VER()) });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -40,21 +39,14 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||||
try {
|
if (oldVersion != newVersion) {
|
||||||
if (oldVersion < 3) {
|
// Nuke old DB and create new table
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
|
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
|
||||||
db.execSQL(
|
db.execSQL(
|
||||||
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
|
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
|
||||||
"(id TEXT, name TEXT, version TEXT, versionCode INT, minMagisk INT, " +
|
"(id TEXT, name TEXT, version TEXT, versionCode INT, minMagisk INT, " +
|
||||||
"author TEXT, description TEXT, repo_name TEXT, last_update INT, " +
|
"author TEXT, description TEXT, last_update INT, PRIMARY KEY(id))");
|
||||||
"PRIMARY KEY(id))");
|
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
|
||||||
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
|
|
||||||
oldVersion = 3;
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
// Reset database
|
|
||||||
onDowngrade(db, DATABASE_VER, 0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,8 +67,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void removeRepo(Repo repo) {
|
public void removeRepo(Repo repo) {
|
||||||
mDb.delete(TABLE_NAME, "repo_name=?", new String[] { repo.getRepoName() });
|
removeRepo(repo.getId());
|
||||||
notifyAdapter();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeRepo(Iterable<String> list) {
|
public void removeRepo(Iterable<String> list) {
|
||||||
@@ -115,7 +106,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
orderBy = "last_update DESC";
|
orderBy = "last_update DESC";
|
||||||
}
|
}
|
||||||
return mDb.query(TABLE_NAME, null, "minMagisk<=? AND minMagisk>=?",
|
return mDb.query(TABLE_NAME, null, "minMagisk<=? AND minMagisk>=?",
|
||||||
new String[] { String.valueOf(Data.magiskVersionCode), String.valueOf(Const.MIN_MODULE_VER()) },
|
new String[] { String.valueOf(Data.magiskVersionCode), String.valueOf(Const.MIN_MODULE_VER) },
|
||||||
null, null, orderBy);
|
null, null, orderBy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -2,27 +2,21 @@ package com.topjohnwu.magisk.fragments;
|
|||||||
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.design.widget.TabLayout;
|
|
||||||
import android.support.v4.view.ViewPager;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.google.android.material.tabs.TabLayout;
|
||||||
import com.topjohnwu.magisk.Data;
|
|
||||||
import com.topjohnwu.magisk.MainActivity;
|
import com.topjohnwu.magisk.MainActivity;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.adapters.TabFragmentAdapter;
|
import com.topjohnwu.magisk.adapters.TabFragmentAdapter;
|
||||||
import com.topjohnwu.magisk.components.BaseFragment;
|
import com.topjohnwu.magisk.components.BaseFragment;
|
||||||
|
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.Unbinder;
|
|
||||||
|
|
||||||
public class LogFragment extends BaseFragment {
|
public class LogFragment extends BaseFragment {
|
||||||
|
|
||||||
private Unbinder unbinder;
|
|
||||||
|
|
||||||
@BindView(R.id.container) ViewPager viewPager;
|
@BindView(R.id.container) ViewPager viewPager;
|
||||||
@BindView(R.id.tab) TabLayout tab;
|
@BindView(R.id.tab) TabLayout tab;
|
||||||
|
|
||||||
@@ -31,15 +25,13 @@ public class LogFragment extends BaseFragment {
|
|||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
// Inflate the layout for this fragment
|
// Inflate the layout for this fragment
|
||||||
View v = inflater.inflate(R.layout.fragment_log, container, false);
|
View v = inflater.inflate(R.layout.fragment_log, container, false);
|
||||||
unbinder = ButterKnife.bind(this, v);
|
unbinder = new LogFragment_ViewBinding(this, v);
|
||||||
|
|
||||||
((MainActivity) requireActivity()).toolbar.setElevation(0);
|
((MainActivity) requireActivity()).toolbar.setElevation(0);
|
||||||
|
|
||||||
TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager());
|
TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager());
|
||||||
|
|
||||||
if (!(Const.USER_ID > 0 && Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
|
adapter.addTab(new SuLogFragment(), getString(R.string.superuser));
|
||||||
adapter.addTab(new SuLogFragment(), getString(R.string.superuser));
|
|
||||||
}
|
|
||||||
adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk));
|
adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk));
|
||||||
tab.setupWithViewPager(viewPager);
|
tab.setupWithViewPager(viewPager);
|
||||||
tab.setVisibility(View.VISIBLE);
|
tab.setVisibility(View.VISIBLE);
|
||||||
@@ -48,11 +40,4 @@ public class LogFragment extends BaseFragment {
|
|||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unbinder.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -5,11 +5,6 @@ import android.content.Context;
|
|||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.StringRes;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.support.v7.widget.CardView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -41,20 +36,22 @@ import com.topjohnwu.magisk.utils.Topic;
|
|||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
import androidx.cardview.widget.CardView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import butterknife.BindColor;
|
import butterknife.BindColor;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.OnClick;
|
import butterknife.OnClick;
|
||||||
import butterknife.Unbinder;
|
|
||||||
|
|
||||||
public class MagiskFragment extends BaseFragment
|
public class MagiskFragment extends BaseFragment
|
||||||
implements SwipeRefreshLayout.OnRefreshListener, ExpandableView, Topic.Subscriber {
|
implements SwipeRefreshLayout.OnRefreshListener, ExpandableView, Topic.Subscriber {
|
||||||
|
|
||||||
private Container expandableContainer = new Container();
|
private Container expandableContainer = new Container();
|
||||||
private Unbinder unbinder;
|
|
||||||
private static boolean shownDialog = false;
|
private static boolean shownDialog = false;
|
||||||
|
|
||||||
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
@BindView(R.id.swipeRefreshLayout) public SwipeRefreshLayout mSwipeRefreshLayout;
|
||||||
|
|
||||||
@BindView(R.id.core_only_notice) CardView coreOnlyNotice;
|
@BindView(R.id.core_only_notice) CardView coreOnlyNotice;
|
||||||
|
|
||||||
@@ -85,7 +82,7 @@ public class MagiskFragment extends BaseFragment
|
|||||||
@BindColor(R.color.red500) int colorBad;
|
@BindColor(R.color.red500) int colorBad;
|
||||||
@BindColor(R.color.green500) int colorOK;
|
@BindColor(R.color.green500) int colorOK;
|
||||||
@BindColor(R.color.yellow500) int colorWarn;
|
@BindColor(R.color.yellow500) int colorWarn;
|
||||||
@BindColor(R.color.grey500) int colorNeutral;
|
@BindColor(R.color.green500) int colorNeutral;
|
||||||
@BindColor(R.color.blue500) int colorInfo;
|
@BindColor(R.color.blue500) int colorInfo;
|
||||||
|
|
||||||
@OnClick(R.id.safetyNet_title)
|
@OnClick(R.id.safetyNet_title)
|
||||||
@@ -136,7 +133,7 @@ public class MagiskFragment extends BaseFragment
|
|||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||||
@Nullable Bundle savedInstanceState) {
|
@Nullable Bundle savedInstanceState) {
|
||||||
View v = inflater.inflate(R.layout.fragment_magisk, container, false);
|
View v = inflater.inflate(R.layout.fragment_magisk, container, false);
|
||||||
unbinder = ButterKnife.bind(this, v);
|
unbinder = new MagiskFragment_ViewBinding(this, v);
|
||||||
requireActivity().setTitle(R.string.magisk);
|
requireActivity().setTitle(R.string.magisk);
|
||||||
|
|
||||||
expandableContainer.expandLayout = expandLayout;
|
expandableContainer.expandLayout = expandLayout;
|
||||||
@@ -196,12 +193,6 @@ public class MagiskFragment extends BaseFragment
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unbinder.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Container getContainer() {
|
public Container getContainer() {
|
||||||
return expandableContainer;
|
return expandableContainer;
|
||||||
@@ -223,11 +214,10 @@ public class MagiskFragment extends BaseFragment
|
|||||||
|
|
||||||
boolean hasNetwork = Download.checkNetworkStatus(mm);
|
boolean hasNetwork = Download.checkNetworkStatus(mm);
|
||||||
boolean hasRoot = Shell.rootAccess();
|
boolean hasRoot = Shell.rootAccess();
|
||||||
boolean isUpToDate = Data.magiskVersionCode > Const.MAGISK_VER.UNIFIED;
|
|
||||||
|
|
||||||
magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
|
magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
|
||||||
installOptionCard.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
|
installOptionCard.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
|
||||||
uninstallButton.setVisibility(isUpToDate && hasRoot ? View.VISIBLE : View.GONE);
|
uninstallButton.setVisibility(hasRoot ? View.VISIBLE : View.GONE);
|
||||||
coreOnlyNotice.setVisibility(mm.prefs.getBoolean(Const.Key.COREONLY, false) ? View.VISIBLE : View.GONE);
|
coreOnlyNotice.setVisibility(mm.prefs.getBoolean(Const.Key.COREONLY, false) ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
int image, color;
|
int image, color;
|
||||||
|
@@ -1,10 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.fragments;
|
package com.topjohnwu.magisk.fragments;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -17,13 +13,14 @@ import com.topjohnwu.magisk.adapters.ApplicationAdapter;
|
|||||||
import com.topjohnwu.magisk.components.BaseFragment;
|
import com.topjohnwu.magisk.components.BaseFragment;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.Unbinder;
|
|
||||||
|
|
||||||
public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber {
|
public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber {
|
||||||
|
|
||||||
private Unbinder unbinder;
|
|
||||||
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
||||||
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
||||||
SearchView search;
|
SearchView search;
|
||||||
@@ -42,7 +39,7 @@ public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber
|
|||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false);
|
View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false);
|
||||||
unbinder = ButterKnife.bind(this, view);
|
unbinder = new MagiskHideFragment_ViewBinding(this, view);
|
||||||
|
|
||||||
appAdapter = new ApplicationAdapter(requireActivity());
|
appAdapter = new ApplicationAdapter(requireActivity());
|
||||||
recyclerView.setAdapter(appAdapter);
|
recyclerView.setAdapter(appAdapter);
|
||||||
@@ -76,12 +73,6 @@ public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber
|
|||||||
search.setOnQueryTextListener(searchListener);
|
search.setOnQueryTextListener(searchListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unbinder.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int[] getSubscribedTopics() {
|
public int[] getSubscribedTopics() {
|
||||||
return new int[] {Topic.MAGISK_HIDE_DONE};
|
return new int[] {Topic.MAGISK_HIDE_DONE};
|
||||||
|
@@ -2,8 +2,6 @@ package com.topjohnwu.magisk.fragments;
|
|||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.design.widget.Snackbar;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
@@ -16,11 +14,11 @@ import android.widget.ProgressBar;
|
|||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.components.BaseFragment;
|
import com.topjohnwu.magisk.components.BaseFragment;
|
||||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.utils.Download;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
@@ -28,14 +26,11 @@ import java.io.File;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.Unbinder;
|
|
||||||
|
|
||||||
public class MagiskLogFragment extends BaseFragment {
|
public class MagiskLogFragment extends BaseFragment {
|
||||||
|
|
||||||
private Unbinder unbinder;
|
|
||||||
|
|
||||||
@BindView(R.id.txtLog) TextView txtLog;
|
@BindView(R.id.txtLog) TextView txtLog;
|
||||||
@BindView(R.id.svLog) ScrollView svLog;
|
@BindView(R.id.svLog) ScrollView svLog;
|
||||||
@BindView(R.id.hsvLog) HorizontalScrollView hsvLog;
|
@BindView(R.id.hsvLog) HorizontalScrollView hsvLog;
|
||||||
@@ -45,7 +40,7 @@ public class MagiskLogFragment extends BaseFragment {
|
|||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_magisk_log, container, false);
|
View view = inflater.inflate(R.layout.fragment_magisk_log, container, false);
|
||||||
unbinder = ButterKnife.bind(this, view);
|
unbinder = new MagiskLogFragment_ViewBinding(this, view);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
txtLog.setTextIsSelectable(true);
|
txtLog.setTextIsSelectable(true);
|
||||||
return view;
|
return view;
|
||||||
@@ -63,12 +58,6 @@ public class MagiskLogFragment extends BaseFragment {
|
|||||||
readLogs();
|
readLogs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unbinder.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.menu_log, menu);
|
inflater.inflate(R.menu.menu_log, menu);
|
||||||
@@ -91,7 +80,7 @@ public class MagiskLogFragment extends BaseFragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readLogs() {
|
private void readLogs() {
|
||||||
Shell.su("cat " + Const.MAGISK_LOG + " | tail -n 5000").submit(result -> {
|
Shell.su("cat " + Const.MAGISK_LOG + " | tail -n 5000").submit(result -> {
|
||||||
progressBar.setVisibility(View.GONE);
|
progressBar.setVisibility(View.GONE);
|
||||||
if (result.getOut().isEmpty())
|
if (result.getOut().isEmpty())
|
||||||
@@ -103,14 +92,14 @@ public class MagiskLogFragment extends BaseFragment {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveLogs() {
|
private void saveLogs() {
|
||||||
Calendar now = Calendar.getInstance();
|
Calendar now = Calendar.getInstance();
|
||||||
String filename = Utils.fmt("magisk_log_%04d%02d%02d_%02d%02d%02d.log",
|
String filename = Utils.fmt("magisk_log_%04d%02d%02d_%02d%02d%02d.log",
|
||||||
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
|
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
|
||||||
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
||||||
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
|
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
|
||||||
|
|
||||||
File logFile = new File(Download.EXTERNAL_PATH, filename);
|
File logFile = new File(Const.EXTERNAL_PATH, filename);
|
||||||
try {
|
try {
|
||||||
logFile.createNewFile();
|
logFile.createNewFile();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
@@ -121,7 +110,7 @@ public class MagiskLogFragment extends BaseFragment {
|
|||||||
SnackbarMaker.make(txtLog, logFile.getPath(), Snackbar.LENGTH_SHORT).show());
|
SnackbarMaker.make(txtLog, logFile.getPath(), Snackbar.LENGTH_SHORT).show());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearLogs() {
|
private void clearLogs() {
|
||||||
Shell.su("echo -n > " + Const.MAGISK_LOG).submit();
|
Shell.su("echo -n > " + Const.MAGISK_LOG).submit();
|
||||||
txtLog.setText(R.string.log_is_empty);
|
txtLog.setText(R.string.log_is_empty);
|
||||||
SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
|
SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
|
||||||
|
@@ -4,10 +4,6 @@ import android.Manifest;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -17,6 +13,7 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.FlashActivity;
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.adapters.ModulesAdapter;
|
import com.topjohnwu.magisk.adapters.ModulesAdapter;
|
||||||
@@ -30,19 +27,21 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.OnClick;
|
import butterknife.OnClick;
|
||||||
import butterknife.Unbinder;
|
|
||||||
|
|
||||||
public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
|
public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
|
||||||
|
|
||||||
private Unbinder unbinder;
|
|
||||||
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
||||||
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
||||||
@BindView(R.id.empty_rv) TextView emptyRv;
|
@BindView(R.id.empty_rv) TextView emptyRv;
|
||||||
|
|
||||||
@OnClick(R.id.fab)
|
@OnClick(R.id.fab)
|
||||||
public void selectFile() {
|
void selectFile() {
|
||||||
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
|
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
|
||||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
||||||
intent.setType("application/zip");
|
intent.setType("application/zip");
|
||||||
@@ -56,7 +55,7 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
|
|||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_modules, container, false);
|
View view = inflater.inflate(R.layout.fragment_modules, container, false);
|
||||||
unbinder = ButterKnife.bind(this, view);
|
unbinder = new ModulesFragment_ViewBinding(this, view);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
mSwipeRefreshLayout.setOnRefreshListener(() -> {
|
mSwipeRefreshLayout.setOnRefreshListener(() -> {
|
||||||
@@ -95,18 +94,12 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
|
|||||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
|
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
|
||||||
// Get the URI of the selected file
|
// Get the URI of the selected file
|
||||||
Intent intent = new Intent(getActivity(), FlashActivity.class);
|
Intent intent = new Intent(getActivity(), Data.classMap.get(FlashActivity.class));
|
||||||
intent.setData(data.getData()).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
intent.setData(data.getData()).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unbinder.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.menu_reboot, menu);
|
inflater.inflate(R.menu.menu_reboot, menu);
|
||||||
@@ -125,7 +118,7 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
|
|||||||
Shell.su("/system/bin/reboot bootloader").submit();
|
Shell.su("/system/bin/reboot bootloader").submit();
|
||||||
return true;
|
return true;
|
||||||
case R.id.reboot_download:
|
case R.id.reboot_download:
|
||||||
Shell.su("/system/bin/reboot download").submit();
|
Shell.su("/system/bin/reboot upgrade").submit();
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@@ -2,10 +2,6 @@ package com.topjohnwu.magisk.fragments;
|
|||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -26,13 +22,14 @@ import com.topjohnwu.magisk.utils.Topic;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.Unbinder;
|
|
||||||
|
|
||||||
public class ReposFragment extends BaseFragment implements Topic.Subscriber {
|
public class ReposFragment extends BaseFragment implements Topic.Subscriber {
|
||||||
|
|
||||||
private Unbinder unbinder;
|
|
||||||
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
||||||
@BindView(R.id.empty_rv) TextView emptyRv;
|
@BindView(R.id.empty_rv) TextView emptyRv;
|
||||||
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
||||||
@@ -49,7 +46,7 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
|
|||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_repos, container, false);
|
View view = inflater.inflate(R.layout.fragment_repos, container, false);
|
||||||
unbinder = ButterKnife.bind(this, view);
|
unbinder = new ReposFragment_ViewBinding(this, view);
|
||||||
|
|
||||||
mSwipeRefreshLayout.setRefreshing(true);
|
mSwipeRefreshLayout.setRefreshing(true);
|
||||||
recyclerView.setVisibility(View.GONE);
|
recyclerView.setVisibility(View.GONE);
|
||||||
@@ -123,6 +120,5 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
|
|||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
mm.repoDB.unregisterAdapter();
|
mm.repoDB.unregisterAdapter();
|
||||||
unbinder.unbind();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,85 +1,110 @@
|
|||||||
package com.topjohnwu.magisk.fragments;
|
package com.topjohnwu.magisk.fragments;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.hardware.fingerprint.FingerprintManager;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v14.preference.SwitchPreference;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.support.v7.preference.ListPreference;
|
|
||||||
import android.support.v7.preference.Preference;
|
|
||||||
import android.support.v7.preference.PreferenceCategory;
|
|
||||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
|
||||||
import android.support.v7.preference.PreferenceScreen;
|
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
||||||
import com.topjohnwu.magisk.asyncs.PatchAPK;
|
import com.topjohnwu.magisk.asyncs.PatchAPK;
|
||||||
import com.topjohnwu.magisk.components.CustomAlertDialog;
|
import com.topjohnwu.magisk.utils.DlInstallManager;
|
||||||
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
|
||||||
import com.topjohnwu.magisk.utils.Download;
|
import com.topjohnwu.magisk.utils.Download;
|
||||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||||
import com.topjohnwu.magisk.utils.LocaleManager;
|
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||||
import com.topjohnwu.magisk.utils.RootUtils;
|
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
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 java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
import androidx.preference.ListPreference;
|
||||||
|
import androidx.preference.Preference;
|
||||||
|
import androidx.preference.PreferenceCategory;
|
||||||
|
import androidx.preference.PreferenceFragmentCompat;
|
||||||
|
import androidx.preference.PreferenceGroupAdapter;
|
||||||
|
import androidx.preference.PreferenceScreen;
|
||||||
|
import androidx.preference.PreferenceViewHolder;
|
||||||
|
import androidx.preference.SwitchPreference;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
public class SettingsFragment extends PreferenceFragmentCompat
|
public class SettingsFragment extends PreferenceFragmentCompat
|
||||||
implements SharedPreferences.OnSharedPreferenceChangeListener,
|
implements SharedPreferences.OnSharedPreferenceChangeListener,
|
||||||
Topic.Subscriber, Topic.AutoSubscriber {
|
Topic.Subscriber, Topic.AutoSubscriber {
|
||||||
|
|
||||||
private PreferenceScreen prefScreen;
|
|
||||||
|
|
||||||
private ListPreference updateChannel, suAccess, autoRes, suNotification,
|
|
||||||
requestTimeout, multiuserMode, namespaceMode;
|
|
||||||
private MagiskManager mm;
|
private MagiskManager mm;
|
||||||
private PreferenceCategory generalCatagory;
|
|
||||||
|
private ListPreference updateChannel, autoRes, suNotification,
|
||||||
|
requestTimeout, rootConfig, multiuserConfig, nsConfig;
|
||||||
|
|
||||||
|
private int rootState, namespaceState;
|
||||||
|
private boolean showSuperuser;
|
||||||
|
|
||||||
|
private void prefsSync() {
|
||||||
|
rootState = mm.mDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
|
||||||
|
namespaceState = mm.mDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
|
||||||
|
showSuperuser = Utils.showSuperUser();
|
||||||
|
mm.prefs.edit()
|
||||||
|
.putString(Const.Key.ROOT_ACCESS, String.valueOf(rootState))
|
||||||
|
.putString(Const.Key.SU_MNT_NS, String.valueOf(namespaceState))
|
||||||
|
.putString(Const.Key.SU_MULTIUSER_MODE, String.valueOf(Data.multiuserState))
|
||||||
|
.putBoolean(Const.Key.SU_FINGERPRINT, FingerprintHelper.useFingerPrint())
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||||
setPreferencesFromResource(R.xml.app_settings, rootKey);
|
|
||||||
mm = Data.MM();
|
mm = Data.MM();
|
||||||
prefScreen = getPreferenceScreen();
|
prefsSync();
|
||||||
|
|
||||||
generalCatagory = (PreferenceCategory) findPreference("general");
|
setPreferencesFromResource(R.xml.app_settings, rootKey);
|
||||||
|
|
||||||
|
PreferenceScreen prefScreen = getPreferenceScreen();
|
||||||
|
|
||||||
|
PreferenceCategory generalCatagory = (PreferenceCategory) findPreference("general");
|
||||||
PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk");
|
PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk");
|
||||||
PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser");
|
PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser");
|
||||||
Preference hideManager = findPreference("hide");
|
Preference hideManager = findPreference("hide");
|
||||||
|
hideManager.setOnPreferenceClickListener(pref -> {
|
||||||
|
PatchAPK.hideManager();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
Preference restoreManager = findPreference("restore");
|
Preference restoreManager = findPreference("restore");
|
||||||
findPreference("clear").setOnPreferenceClickListener((pref) -> {
|
restoreManager.setOnPreferenceClickListener(pref -> {
|
||||||
|
DlInstallManager.restore();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
findPreference("clear").setOnPreferenceClickListener(pref -> {
|
||||||
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
|
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
|
||||||
mm.repoDB.clearRepo();
|
mm.repoDB.clearRepo();
|
||||||
Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT);
|
Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
findPreference("hosts").setOnPreferenceClickListener(pref -> {
|
||||||
|
Shell.su("add_hosts_module").exec();
|
||||||
|
Utils.loadModules();
|
||||||
|
Utils.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
updateChannel = (ListPreference) findPreference(Const.Key.UPDATE_CHANNEL);
|
updateChannel = (ListPreference) findPreference(Const.Key.UPDATE_CHANNEL);
|
||||||
suAccess = (ListPreference) findPreference(Const.Key.ROOT_ACCESS);
|
rootConfig = (ListPreference) findPreference(Const.Key.ROOT_ACCESS);
|
||||||
autoRes = (ListPreference) findPreference(Const.Key.SU_AUTO_RESPONSE);
|
autoRes = (ListPreference) findPreference(Const.Key.SU_AUTO_RESPONSE);
|
||||||
requestTimeout = (ListPreference) findPreference(Const.Key.SU_REQUEST_TIMEOUT);
|
requestTimeout = (ListPreference) findPreference(Const.Key.SU_REQUEST_TIMEOUT);
|
||||||
suNotification = (ListPreference) findPreference(Const.Key.SU_NOTIFICATION);
|
suNotification = (ListPreference) findPreference(Const.Key.SU_NOTIFICATION);
|
||||||
multiuserMode = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE);
|
multiuserConfig = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE);
|
||||||
namespaceMode = (ListPreference) findPreference(Const.Key.SU_MNT_NS);
|
nsConfig = (ListPreference) findPreference(Const.Key.SU_MNT_NS);
|
||||||
SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH);
|
SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH);
|
||||||
SwitchPreference fingerprint = (SwitchPreference) findPreference(Const.Key.SU_FINGERPRINT);
|
SwitchPreference fingerprint = (SwitchPreference) findPreference(Const.Key.SU_FINGERPRINT);
|
||||||
|
|
||||||
@@ -109,7 +134,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
|
|
||||||
// Disable dangerous settings in secondary user
|
// Disable dangerous settings in secondary user
|
||||||
if (Const.USER_ID > 0) {
|
if (Const.USER_ID > 0) {
|
||||||
suCategory.removePreference(multiuserMode);
|
suCategory.removePreference(multiuserConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable re-authentication option on Android O, it will not work
|
// Disable re-authentication option on Android O, it will not work
|
||||||
@@ -126,38 +151,12 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
fingerprint.setSummary(R.string.disable_fingerprint);
|
fingerprint.setSummary(R.string.disable_fingerprint);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Data.magiskVersionCode >= Const.MAGISK_VER.MANAGER_HIDE) {
|
if (Shell.rootAccess() && Const.USER_ID == 0) {
|
||||||
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
if (mm.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
|
||||||
hideManager.setOnPreferenceClickListener((pref) -> {
|
|
||||||
PatchAPK.hideManager(requireActivity());
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
generalCatagory.removePreference(restoreManager);
|
generalCatagory.removePreference(restoreManager);
|
||||||
} else {
|
} else {
|
||||||
if (Download.checkNetworkStatus(mm)) {
|
if (!Download.checkNetworkStatus(mm))
|
||||||
restoreManager.setOnPreferenceClickListener((pref) -> {
|
|
||||||
Download.receive(
|
|
||||||
requireActivity(), new DownloadReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onDownloadDone(Context context, Uri uri) {
|
|
||||||
Data.exportPrefs();
|
|
||||||
Shell.su("cp " + uri.getPath() + " /data/local/tmp/manager.apk").exec();
|
|
||||||
if (ShellUtils.fastCmdResult("pm install /data/local/tmp/manager.apk")) {
|
|
||||||
Shell.su("rm -f /data/local/tmp/manager.apk").exec();
|
|
||||||
RootUtils.uninstallPkg(context.getPackageName());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Shell.su("rm -f /data/local/tmp/manager.apk").exec();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Data.managerLink,
|
|
||||||
Utils.fmt("MagiskManager-v%s.apk", Data.remoteManagerVersionString)
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
generalCatagory.removePreference(restoreManager);
|
generalCatagory.removePreference(restoreManager);
|
||||||
}
|
|
||||||
generalCatagory.removePreference(hideManager);
|
generalCatagory.removePreference(hideManager);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -165,16 +164,13 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
generalCatagory.removePreference(hideManager);
|
generalCatagory.removePreference(hideManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Shell.rootAccess() || (Const.USER_ID > 0 &&
|
if (!showSuperuser) {
|
||||||
Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
|
|
||||||
prefScreen.removePreference(suCategory);
|
prefScreen.removePreference(suCategory);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Shell.rootAccess()) {
|
if (!Shell.rootAccess()) {
|
||||||
prefScreen.removePreference(magiskCategory);
|
prefScreen.removePreference(magiskCategory);
|
||||||
generalCatagory.removePreference(hideManager);
|
generalCatagory.removePreference(hideManager);
|
||||||
} else if (Data.magiskVersionCode < Const.MAGISK_VER.UNIFIED) {
|
|
||||||
prefScreen.removePreference(magiskCategory);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,11 +213,18 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
mm.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
|
mm.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Data.loadConfig();
|
|
||||||
setSummary();
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
|
case Const.Key.ROOT_ACCESS:
|
||||||
|
rootState = Utils.getPrefsInt(prefs, key);
|
||||||
|
break;
|
||||||
|
case Const.Key.SU_MULTIUSER_MODE:
|
||||||
|
Data.multiuserState = Utils.getPrefsInt(prefs, key);
|
||||||
|
break;
|
||||||
|
case Const.Key.SU_MNT_NS:
|
||||||
|
namespaceState = Utils.getPrefsInt(prefs, key);
|
||||||
|
break;
|
||||||
case Const.Key.DARK_THEME:
|
case Const.Key.DARK_THEME:
|
||||||
Topic.publish(false, Topic.RELOAD_ACTIVITY);
|
requireActivity().recreate();
|
||||||
break;
|
break;
|
||||||
case Const.Key.COREONLY:
|
case Const.Key.COREONLY:
|
||||||
if (prefs.getBoolean(key, false)) {
|
if (prefs.getBoolean(key, false)) {
|
||||||
@@ -240,20 +243,9 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
Shell.su("magiskhide --disable").submit();
|
Shell.su("magiskhide --disable").submit();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Const.Key.HOSTS:
|
|
||||||
if (prefs.getBoolean(key, false)) {
|
|
||||||
Shell.su("cp -af /system/etc/hosts " + Const.MAGISK_HOST_FILE,
|
|
||||||
"mount -o bind " + Const.MAGISK_HOST_FILE + " /system/etc/hosts")
|
|
||||||
.submit();
|
|
||||||
} else {
|
|
||||||
Shell.su("umount -l /system/etc/hosts",
|
|
||||||
"rm -f " + Const.MAGISK_HOST_FILE)
|
|
||||||
.submit();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Const.Key.LOCALE:
|
case Const.Key.LOCALE:
|
||||||
LocaleManager.setLocale(mm);
|
LocaleManager.setLocale(mm);
|
||||||
Topic.publish(false, Topic.RELOAD_ACTIVITY);
|
requireActivity().recreate();
|
||||||
break;
|
break;
|
||||||
case Const.Key.UPDATE_CHANNEL:
|
case Const.Key.UPDATE_CHANNEL:
|
||||||
case Const.Key.CUSTOM_CHANNEL:
|
case Const.Key.CUSTOM_CHANNEL:
|
||||||
@@ -263,6 +255,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
Utils.setupUpdateCheck();
|
Utils.setupUpdateCheck();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Data.loadConfig();
|
||||||
|
setSummary();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -272,54 +266,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
case Const.Key.SU_FINGERPRINT:
|
case Const.Key.SU_FINGERPRINT:
|
||||||
boolean checked = ((SwitchPreference) preference).isChecked();
|
boolean checked = ((SwitchPreference) preference).isChecked();
|
||||||
((SwitchPreference) preference).setChecked(!checked);
|
((SwitchPreference) preference).setChecked(!checked);
|
||||||
CustomAlertDialog dialog = new CustomAlertDialog(requireActivity());
|
FingerprintHelper.showAuthDialog(requireActivity(), () -> {
|
||||||
CustomAlertDialog.ViewHolder vh = dialog.getViewHolder();
|
((SwitchPreference) preference).setChecked(checked);
|
||||||
Drawable fingerprint = getResources().getDrawable(R.drawable.ic_fingerprint);
|
mm.mDB.setSettings(key, checked ? 1 : 0);
|
||||||
fingerprint.setBounds(0, 0, Utils.dpInPx(50), Utils.dpInPx(50));
|
});
|
||||||
Resources.Theme theme = requireActivity().getTheme();
|
|
||||||
TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint});
|
|
||||||
fingerprint.setTint(ta.getColor(0, Color.GRAY));
|
|
||||||
ta.recycle();
|
|
||||||
vh.messageView.setCompoundDrawables(null, null, null, fingerprint);
|
|
||||||
vh.messageView.setCompoundDrawablePadding(Utils.dpInPx(20));
|
|
||||||
vh.messageView.setGravity(Gravity.CENTER);
|
|
||||||
try {
|
|
||||||
FingerprintHelper helper = new FingerprintHelper() {
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationError(int errorCode, CharSequence errString) {
|
|
||||||
vh.messageView.setTextColor(Color.RED);
|
|
||||||
vh.messageView.setText(errString);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
|
|
||||||
vh.messageView.setTextColor(Color.RED);
|
|
||||||
vh.messageView.setText(helpString);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationFailed() {
|
|
||||||
vh.messageView.setTextColor(Color.RED);
|
|
||||||
vh.messageView.setText(R.string.auth_fail);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
|
|
||||||
dialog.dismiss();
|
|
||||||
((SwitchPreference) preference).setChecked(checked);
|
|
||||||
mm.mDB.setSettings(key, checked ? 1 : 0);
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
|
||||||
dialog.setMessage(R.string.auth_fingerprint)
|
|
||||||
.setNegativeButton(R.string.close, (d, w) -> helper.cancel())
|
|
||||||
.setOnCancelListener(d -> helper.cancel())
|
|
||||||
.show();
|
|
||||||
helper.authenticate();
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -328,8 +278,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
private void setSummary() {
|
private void setSummary() {
|
||||||
updateChannel.setSummary(getResources()
|
updateChannel.setSummary(getResources()
|
||||||
.getStringArray(R.array.update_channel)[Data.updateChannel]);
|
.getStringArray(R.array.update_channel)[Data.updateChannel]);
|
||||||
suAccess.setSummary(getResources()
|
rootConfig.setSummary(getResources()
|
||||||
.getStringArray(R.array.su_access)[Data.suAccessState]);
|
.getStringArray(R.array.su_access)[rootState]);
|
||||||
autoRes.setSummary(getResources()
|
autoRes.setSummary(getResources()
|
||||||
.getStringArray(R.array.auto_response)[Data.suResponseType]);
|
.getStringArray(R.array.auto_response)[Data.suResponseType]);
|
||||||
suNotification.setSummary(getResources()
|
suNotification.setSummary(getResources()
|
||||||
@@ -337,10 +287,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
requestTimeout.setSummary(
|
requestTimeout.setSummary(
|
||||||
getString(R.string.request_timeout_summary,
|
getString(R.string.request_timeout_summary,
|
||||||
mm.prefs.getString(Const.Key.SU_REQUEST_TIMEOUT, "10")));
|
mm.prefs.getString(Const.Key.SU_REQUEST_TIMEOUT, "10")));
|
||||||
multiuserMode.setSummary(getResources()
|
multiuserConfig.setSummary(getResources()
|
||||||
.getStringArray(R.array.multiuser_summary)[Data.multiuserMode]);
|
.getStringArray(R.array.multiuser_summary)[Data.multiuserState]);
|
||||||
namespaceMode.setSummary(getResources()
|
nsConfig.setSummary(getResources()
|
||||||
.getStringArray(R.array.namespace_summary)[Data.suNamespaceMode]);
|
.getStringArray(R.array.namespace_summary)[namespaceState]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -352,4 +302,38 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
public int[] getSubscribedTopics() {
|
public int[] getSubscribedTopics() {
|
||||||
return new int[] {Topic.LOCALE_FETCH_DONE};
|
return new int[] {Topic.LOCALE_FETCH_DONE};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected RecyclerView.Adapter onCreateAdapter(PreferenceScreen preferenceScreen) {
|
||||||
|
return new PreferenceGroupAdapter(preferenceScreen) {
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(PreferenceViewHolder holder, int position) {
|
||||||
|
super.onBindViewHolder(holder, position);
|
||||||
|
Preference preference = getItem(position);
|
||||||
|
if (preference instanceof PreferenceCategory)
|
||||||
|
setZeroPaddingToLayoutChildren(holder.itemView);
|
||||||
|
else {
|
||||||
|
View iconFrame = holder.itemView.findViewById(R.id.icon_frame);
|
||||||
|
if (iconFrame != null) {
|
||||||
|
iconFrame.setVisibility(preference.getIcon() == null ? View.GONE : View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setZeroPaddingToLayoutChildren(View view) {
|
||||||
|
if (!(view instanceof ViewGroup))
|
||||||
|
return;
|
||||||
|
ViewGroup viewGroup = (ViewGroup) view;
|
||||||
|
int childCount = viewGroup.getChildCount();
|
||||||
|
for (int i = 0; i < childCount; i++) {
|
||||||
|
setZeroPaddingToLayoutChildren(viewGroup.getChildAt(i));
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||||
|
viewGroup.setPaddingRelative(0, viewGroup.getPaddingTop(), viewGroup.getPaddingEnd(), viewGroup.getPaddingBottom());
|
||||||
|
else
|
||||||
|
viewGroup.setPadding(0, viewGroup.getPaddingTop(), viewGroup.getPaddingRight(), viewGroup.getPaddingBottom());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.fragments;
|
package com.topjohnwu.magisk.fragments;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
@@ -15,16 +13,15 @@ import com.topjohnwu.magisk.R;
|
|||||||
import com.topjohnwu.magisk.adapters.SuLogAdapter;
|
import com.topjohnwu.magisk.adapters.SuLogAdapter;
|
||||||
import com.topjohnwu.magisk.components.BaseFragment;
|
import com.topjohnwu.magisk.components.BaseFragment;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.Unbinder;
|
|
||||||
|
|
||||||
public class SuLogFragment extends BaseFragment {
|
public class SuLogFragment extends BaseFragment {
|
||||||
|
|
||||||
@BindView(R.id.empty_rv) TextView emptyRv;
|
@BindView(R.id.empty_rv) TextView emptyRv;
|
||||||
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
||||||
|
|
||||||
private Unbinder unbinder;
|
|
||||||
private SuLogAdapter adapter;
|
private SuLogAdapter adapter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -44,7 +41,7 @@ public class SuLogFragment extends BaseFragment {
|
|||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
// Inflate the layout for this fragment
|
// Inflate the layout for this fragment
|
||||||
View v = inflater.inflate(R.layout.fragment_su_log, container, false);
|
View v = inflater.inflate(R.layout.fragment_su_log, container, false);
|
||||||
unbinder = ButterKnife.bind(this, v);
|
unbinder = new SuLogFragment_ViewBinding(this, v);
|
||||||
adapter = new SuLogAdapter(mm.mDB);
|
adapter = new SuLogAdapter(mm.mDB);
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
@@ -79,10 +76,4 @@ public class SuLogFragment extends BaseFragment {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unbinder.unbind();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -2,8 +2,6 @@ package com.topjohnwu.magisk.fragments;
|
|||||||
|
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v7.widget.RecyclerView;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -16,24 +14,24 @@ import com.topjohnwu.magisk.container.Policy;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.Unbinder;
|
|
||||||
|
|
||||||
public class SuperuserFragment extends BaseFragment {
|
public class SuperuserFragment extends BaseFragment {
|
||||||
|
|
||||||
private Unbinder unbinder;
|
|
||||||
private PackageManager pm;
|
|
||||||
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
||||||
@BindView(R.id.empty_rv) TextView emptyRv;
|
@BindView(R.id.empty_rv) TextView emptyRv;
|
||||||
|
|
||||||
|
private PackageManager pm;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||||
View view = inflater.inflate(R.layout.fragment_superuser, container, false);
|
View view = inflater.inflate(R.layout.fragment_superuser, container, false);
|
||||||
unbinder = ButterKnife.bind(this, view);
|
unbinder = new SuperuserFragment_ViewBinding(this, view);
|
||||||
|
|
||||||
pm = getActivity().getPackageManager();
|
pm = requireActivity().getPackageManager();
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,14 +47,8 @@ public class SuperuserFragment extends BaseFragment {
|
|||||||
displayPolicyList();
|
displayPolicyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unbinder.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void displayPolicyList() {
|
private void displayPolicyList() {
|
||||||
List<Policy> policyList = mm.mDB.getPolicyList(pm);
|
List<Policy> policyList = mm.mDB.getPolicyList();
|
||||||
|
|
||||||
if (policyList.size() == 0) {
|
if (policyList.size() == 0) {
|
||||||
emptyRv.setVisibility(View.VISIBLE);
|
emptyRv.setVisibility(View.VISIBLE);
|
||||||
|
@@ -1,18 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.receivers;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.services.OnBootService;
|
|
||||||
|
|
||||||
public class BootReceiver extends BroadcastReceiver {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (TextUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED))
|
|
||||||
OnBootService.enqueueWork(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@@ -0,0 +1,76 @@
|
|||||||
|
package com.topjohnwu.magisk.receivers;
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.SuRequestActivity;
|
||||||
|
import com.topjohnwu.magisk.services.OnBootService;
|
||||||
|
import com.topjohnwu.magisk.utils.DlInstallManager;
|
||||||
|
import com.topjohnwu.magisk.utils.SuConnector;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
|
public class GeneralReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
|
private String getPkg(Intent i) {
|
||||||
|
return i.getData() == null ? "" : i.getData().getEncodedSchemeSpecificPart();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceive(Context context, Intent intent) {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
if (intent == null)
|
||||||
|
return;
|
||||||
|
String action = intent.getAction();
|
||||||
|
if (action == null)
|
||||||
|
return;
|
||||||
|
switch (action) {
|
||||||
|
case Intent.ACTION_BOOT_COMPLETED:
|
||||||
|
String bootAction = intent.getStringExtra("action");
|
||||||
|
if (bootAction == null)
|
||||||
|
bootAction = "boot";
|
||||||
|
switch (bootAction) {
|
||||||
|
case "request":
|
||||||
|
Intent i = new Intent(mm, Data.classMap.get(SuRequestActivity.class))
|
||||||
|
.putExtra("socket", intent.getStringExtra("socket"))
|
||||||
|
.putExtra("version", 2)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
mm.startActivity(i);
|
||||||
|
break;
|
||||||
|
case "log":
|
||||||
|
SuConnector.handleLogs(intent, 2);
|
||||||
|
break;
|
||||||
|
case "notify":
|
||||||
|
SuConnector.handleNotify(intent);
|
||||||
|
break;
|
||||||
|
case "boot":
|
||||||
|
default:
|
||||||
|
/* The actual on-boot trigger */
|
||||||
|
OnBootService.enqueueWork(mm);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Intent.ACTION_PACKAGE_REPLACED:
|
||||||
|
// This will only work pre-O
|
||||||
|
if (mm.prefs.getBoolean(Const.Key.SU_REAUTH, false)) {
|
||||||
|
mm.mDB.deletePolicy(getPkg(intent));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
|
||||||
|
String pkg = getPkg(intent);
|
||||||
|
mm.mDB.deletePolicy(pkg);
|
||||||
|
Shell.su("magiskhide --rm " + pkg).submit();
|
||||||
|
break;
|
||||||
|
case Const.Key.BROADCAST_MANAGER_UPDATE:
|
||||||
|
Data.managerLink = intent.getStringExtra(Const.Key.INTENT_SET_LINK);
|
||||||
|
DlInstallManager.upgrade(intent.getStringExtra(Const.Key.INTENT_SET_NAME));
|
||||||
|
break;
|
||||||
|
case Const.Key.BROADCAST_REBOOT:
|
||||||
|
Shell.su("/system/bin/reboot").submit();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,49 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.receivers;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.AsyncTask;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
|
||||||
import com.topjohnwu.magisk.asyncs.PatchAPK;
|
|
||||||
import com.topjohnwu.magisk.utils.Download;
|
|
||||||
import com.topjohnwu.utils.JarMap;
|
|
||||||
import com.topjohnwu.utils.SignAPK;
|
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
|
|
||||||
public class ManagerUpdate extends BroadcastReceiver {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
Download.receive(
|
|
||||||
context, new PatchedInstall(),
|
|
||||||
intent.getStringExtra(Const.Key.INTENT_SET_LINK),
|
|
||||||
intent.getStringExtra(Const.Key.INTENT_SET_FILENAME)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class PatchedInstall extends ManagerInstall {
|
|
||||||
@Override
|
|
||||||
public void onDownloadDone(Context context, Uri uri) {
|
|
||||||
if (!context.getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
|
||||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
|
||||||
String orig = uri.getPath();
|
|
||||||
String patch = orig.substring(0, orig.lastIndexOf('.')) + "-patched.apk";
|
|
||||||
try {
|
|
||||||
JarMap apk = new JarMap(orig);
|
|
||||||
PatchAPK.patchPackageID(apk, Const.ORIG_PKG_NAME, context.getPackageName());
|
|
||||||
SignAPK.sign(apk, new BufferedOutputStream(new FileOutputStream(patch)));
|
|
||||||
super.onDownloadDone(context, Uri.fromFile(new File(patch)));
|
|
||||||
} catch (Exception ignored) { }
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
super.onDownloadDone(context, uri);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,32 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.receivers;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
|
||||||
import com.topjohnwu.magisk.Data;
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
|
|
||||||
public class PackageReceiver extends BroadcastReceiver {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
MagiskManager mm = Data.MM();
|
|
||||||
|
|
||||||
String pkg = intent.getData().getEncodedSchemeSpecificPart();
|
|
||||||
|
|
||||||
switch (intent.getAction()) {
|
|
||||||
case Intent.ACTION_PACKAGE_REPLACED:
|
|
||||||
// This will only work pre-O
|
|
||||||
if (mm.prefs.getBoolean(Const.Key.SU_REAUTH, false)) {
|
|
||||||
mm.mDB.deletePolicy(pkg);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
|
|
||||||
mm.mDB.deletePolicy(pkg);
|
|
||||||
Shell.su("magiskhide --rm " + pkg).submit();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,14 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.receivers;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
|
|
||||||
public class RebootReceiver extends BroadcastReceiver {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
Shell.su("/system/bin/reboot").submit();
|
|
||||||
}
|
|
||||||
}
|
|
@@ -7,17 +7,19 @@ import android.content.pm.ShortcutInfo;
|
|||||||
import android.content.pm.ShortcutManager;
|
import android.content.pm.ShortcutManager;
|
||||||
import android.graphics.drawable.Icon;
|
import android.graphics.drawable.Icon;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.RequiresApi;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.SplashActivity;
|
import com.topjohnwu.magisk.SplashActivity;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import androidx.annotation.RequiresApi;
|
||||||
|
|
||||||
public class ShortcutReceiver extends BroadcastReceiver {
|
public class ShortcutReceiver extends BroadcastReceiver {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
@@ -32,11 +34,10 @@ public class ShortcutReceiver extends BroadcastReceiver {
|
|||||||
private ArrayList<ShortcutInfo> getShortCuts(MagiskManager mm) {
|
private ArrayList<ShortcutInfo> getShortCuts(MagiskManager mm) {
|
||||||
ArrayList<ShortcutInfo> shortCuts = new ArrayList<>();
|
ArrayList<ShortcutInfo> shortCuts = new ArrayList<>();
|
||||||
boolean root = Shell.rootAccess();
|
boolean root = Shell.rootAccess();
|
||||||
if (root && !(Const.USER_ID > 0 &&
|
if (Utils.showSuperUser()) {
|
||||||
Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
|
|
||||||
shortCuts.add(new ShortcutInfo.Builder(mm, "superuser")
|
shortCuts.add(new ShortcutInfo.Builder(mm, "superuser")
|
||||||
.setShortLabel(mm.getString(R.string.superuser))
|
.setShortLabel(mm.getString(R.string.superuser))
|
||||||
.setIntent(new Intent(mm, SplashActivity.class)
|
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
|
||||||
.putExtra(Const.Key.OPEN_SECTION, "superuser")
|
.putExtra(Const.Key.OPEN_SECTION, "superuser")
|
||||||
.setAction(Intent.ACTION_VIEW)
|
.setAction(Intent.ACTION_VIEW)
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
||||||
@@ -44,11 +45,10 @@ public class ShortcutReceiver extends BroadcastReceiver {
|
|||||||
.setRank(0)
|
.setRank(0)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
if (root && Data.magiskVersionCode >= Const.MAGISK_VER.UNIFIED
|
if (root && mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
|
||||||
&& mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
|
|
||||||
shortCuts.add(new ShortcutInfo.Builder(mm, "magiskhide")
|
shortCuts.add(new ShortcutInfo.Builder(mm, "magiskhide")
|
||||||
.setShortLabel(mm.getString(R.string.magiskhide))
|
.setShortLabel(mm.getString(R.string.magiskhide))
|
||||||
.setIntent(new Intent(mm, SplashActivity.class)
|
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
|
||||||
.putExtra(Const.Key.OPEN_SECTION, "magiskhide")
|
.putExtra(Const.Key.OPEN_SECTION, "magiskhide")
|
||||||
.setAction(Intent.ACTION_VIEW)
|
.setAction(Intent.ACTION_VIEW)
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
||||||
@@ -59,7 +59,7 @@ public class ShortcutReceiver extends BroadcastReceiver {
|
|||||||
if (!mm.prefs.getBoolean(Const.Key.COREONLY, false) && root && Data.magiskVersionCode >= 0) {
|
if (!mm.prefs.getBoolean(Const.Key.COREONLY, false) && root && Data.magiskVersionCode >= 0) {
|
||||||
shortCuts.add(new ShortcutInfo.Builder(mm, "modules")
|
shortCuts.add(new ShortcutInfo.Builder(mm, "modules")
|
||||||
.setShortLabel(mm.getString(R.string.modules))
|
.setShortLabel(mm.getString(R.string.modules))
|
||||||
.setIntent(new Intent(mm, SplashActivity.class)
|
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
|
||||||
.putExtra(Const.Key.OPEN_SECTION, "modules")
|
.putExtra(Const.Key.OPEN_SECTION, "modules")
|
||||||
.setAction(Intent.ACTION_VIEW)
|
.setAction(Intent.ACTION_VIEW)
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
||||||
@@ -67,8 +67,8 @@ public class ShortcutReceiver extends BroadcastReceiver {
|
|||||||
.setRank(3)
|
.setRank(3)
|
||||||
.build());
|
.build());
|
||||||
shortCuts.add(new ShortcutInfo.Builder(mm, "downloads")
|
shortCuts.add(new ShortcutInfo.Builder(mm, "downloads")
|
||||||
.setShortLabel(mm.getString(R.string.download))
|
.setShortLabel(mm.getString(R.string.downloads))
|
||||||
.setIntent(new Intent(mm, SplashActivity.class)
|
.setIntent(new Intent(mm, Data.classMap.get(SplashActivity.class))
|
||||||
.putExtra(Const.Key.OPEN_SECTION, "downloads")
|
.putExtra(Const.Key.OPEN_SECTION, "downloads")
|
||||||
.setAction(Intent.ACTION_VIEW)
|
.setAction(Intent.ACTION_VIEW)
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
|
||||||
|
@@ -2,18 +2,20 @@ package com.topjohnwu.magisk.services;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v4.app.JobIntentService;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.utils.NotificationMgr;
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.components.Notifications;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.app.JobIntentService;
|
||||||
|
|
||||||
public class OnBootService extends JobIntentService {
|
public class OnBootService extends JobIntentService {
|
||||||
|
|
||||||
public static void enqueueWork(Context context) {
|
public static void enqueueWork(Context context) {
|
||||||
enqueueWork(context, OnBootService.class, Const.ID.ONBOOT_SERVICE_ID, new Intent());
|
enqueueWork(context, Data.classMap.get(OnBootService.class), Const.ID.ONBOOT_SERVICE_ID, new Intent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -26,6 +28,6 @@ public class OnBootService extends JobIntentService {
|
|||||||
* to reboot if dtbo wasn't patched and patched by Magisk Manager.
|
* to reboot if dtbo wasn't patched and patched by Magisk Manager.
|
||||||
* */
|
* */
|
||||||
if (Shell.rootAccess() && ShellUtils.fastCmdResult("mm_patch_dtbo"))
|
if (Shell.rootAccess() && ShellUtils.fastCmdResult("mm_patch_dtbo"))
|
||||||
NotificationMgr.dtboPatched();
|
Notifications.dtboPatched();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,297 +1,21 @@
|
|||||||
package com.topjohnwu.magisk.superuser;
|
package com.topjohnwu.magisk.superuser;
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.hardware.fingerprint.FingerprintManager;
|
|
||||||
import android.net.LocalSocket;
|
|
||||||
import android.net.LocalSocketAddress;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.CountDownTimer;
|
|
||||||
import android.os.FileObserver;
|
|
||||||
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 com.topjohnwu.magisk.Const;
|
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.SuRequestActivity;
|
||||||
import com.topjohnwu.magisk.asyncs.ParallelTask;
|
|
||||||
import com.topjohnwu.magisk.components.BaseActivity;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
import com.topjohnwu.magisk.container.Policy;
|
|
||||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import butterknife.BindView;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class RequestActivity extends BaseActivity {
|
public class RequestActivity 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 String socketPath;
|
|
||||||
private LocalSocket socket;
|
|
||||||
private PackageManager pm;
|
|
||||||
|
|
||||||
private boolean hasTimeout;
|
|
||||||
private Policy policy;
|
|
||||||
private CountDownTimer timer;
|
|
||||||
private FingerprintHelper fingerprintHelper;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDarkTheme() {
|
|
||||||
return R.style.SuRequest_Dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
|
Intent intent = new Intent(this, Data.classMap.get(SuRequestActivity.class))
|
||||||
|
.putExtra("socket", getIntent().getStringExtra("socket"))
|
||||||
pm = getPackageManager();
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
mm.mDB.clearOutdated();
|
startActivity(intent);
|
||||||
|
|
||||||
Intent intent = getIntent();
|
|
||||||
socketPath = intent.getStringExtra("socket");
|
|
||||||
hasTimeout = intent.getBooleanExtra("timeout", true);
|
|
||||||
|
|
||||||
new FileObserver(socketPath) {
|
|
||||||
@Override
|
|
||||||
public void onEvent(int fileEvent, String path) {
|
|
||||||
if (fileEvent == FileObserver.DELETE_SELF) {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.startWatching();
|
|
||||||
|
|
||||||
new SocketManager(this).exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finish() {
|
|
||||||
if (timer != null)
|
|
||||||
timer.cancel();
|
|
||||||
if (fingerprintHelper != null)
|
|
||||||
fingerprintHelper.cancel();
|
|
||||||
super.finish();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean cancelTimeout() {
|
|
||||||
timer.cancel();
|
|
||||||
deny_btn.setText(getString(R.string.deny));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showRequest() {
|
|
||||||
switch (Data.suResponseType) {
|
|
||||||
case Const.Value.SU_AUTO_DENY:
|
|
||||||
handleAction(Policy.DENY, 0);
|
|
||||||
return;
|
|
||||||
case Const.Value.SU_AUTO_ALLOW:
|
|
||||||
handleAction(Policy.ALLOW, 0);
|
|
||||||
return;
|
|
||||||
case Const.Value.SU_PROMPT:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
|
|
||||||
// If not interactive, response directly
|
|
||||||
if (policy.policy != Policy.INTERACTIVE) {
|
|
||||||
handleAction();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setContentView(R.layout.activity_request);
|
|
||||||
ButterKnife.bind(this);
|
|
||||||
|
|
||||||
appIcon.setImageDrawable(policy.info.loadIcon(pm));
|
|
||||||
appNameView.setText(policy.appName);
|
|
||||||
packageNameView.setText(policy.packageName);
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
timer = new CountDownTimer(Data.suRequestTimeout * 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 useFingerprint = Data.suFingerprint && FingerprintHelper.canUseFingerprint();
|
|
||||||
|
|
||||||
if (useFingerprint) {
|
|
||||||
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();
|
|
||||||
useFingerprint = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!useFingerprint) {
|
|
||||||
grant_btn.setOnClickListener(v -> {
|
|
||||||
handleAction(Policy.ALLOW);
|
|
||||||
timer.cancel();
|
|
||||||
});
|
|
||||||
grant_btn.requestFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
grant_btn.setVisibility(useFingerprint ? View.GONE : View.VISIBLE);
|
|
||||||
fingerprintImg.setVisibility(useFingerprint ? View.VISIBLE : View.GONE);
|
|
||||||
|
|
||||||
deny_btn.setOnClickListener(v -> {
|
|
||||||
handleAction(Policy.DENY);
|
|
||||||
timer.cancel();
|
|
||||||
});
|
|
||||||
suPopup.setOnClickListener(v -> cancelTimeout());
|
|
||||||
timeout.setOnTouchListener((v, event) -> cancelTimeout());
|
|
||||||
|
|
||||||
if (hasTimeout) {
|
|
||||||
timer.start();
|
|
||||||
} else {
|
|
||||||
cancelTimeout();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
if (policy != null) {
|
|
||||||
handleAction(Policy.DENY);
|
|
||||||
} else {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleAction() {
|
|
||||||
String response;
|
|
||||||
if (policy.policy == Policy.ALLOW) {
|
|
||||||
response = "socket:ALLOW";
|
|
||||||
} else {
|
|
||||||
response = "socket:DENY";
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
socket.getOutputStream().write((response).getBytes());
|
|
||||||
socket.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
void handleAction(int action) {
|
|
||||||
handleAction(action, Const.Value.timeoutList[timeout.getSelectedItemPosition()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleAction(int action, int time) {
|
|
||||||
policy.policy = action;
|
|
||||||
if (time >= 0) {
|
|
||||||
policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60);
|
|
||||||
mm.mDB.addPolicy(policy);
|
|
||||||
}
|
|
||||||
handleAction();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SocketManager extends ParallelTask<Void, Void, Boolean> {
|
|
||||||
|
|
||||||
SocketManager(BaseActivity context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Boolean doInBackground(Void... params) {
|
|
||||||
try {
|
|
||||||
socket = new LocalSocket();
|
|
||||||
socket.connect(new LocalSocketAddress(socketPath, LocalSocketAddress.Namespace.FILESYSTEM));
|
|
||||||
|
|
||||||
DataInputStream is = new DataInputStream(socket.getInputStream());
|
|
||||||
ContentValues payload = new ContentValues();
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
int nameLen = is.readInt();
|
|
||||||
byte[] nameBytes = new byte[nameLen];
|
|
||||||
is.readFully(nameBytes);
|
|
||||||
String name = new String(nameBytes);
|
|
||||||
if (TextUtils.equals(name, "eof"))
|
|
||||||
break;
|
|
||||||
|
|
||||||
int dataLen = is.readInt();
|
|
||||||
byte[] dataBytes = new byte[dataLen];
|
|
||||||
is.readFully(dataBytes);
|
|
||||||
String data = new String(dataBytes);
|
|
||||||
payload.put(name, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (payload.getAsInteger("uid") == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int uid = payload.getAsInteger("uid");
|
|
||||||
policy = mm.mDB.getPolicy(uid);
|
|
||||||
if (policy == null) {
|
|
||||||
policy = new Policy(uid, pm);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Never allow com.topjohnwu.magisk (could be malware) */
|
|
||||||
if (TextUtils.equals(policy.packageName, Const.ORIG_PKG_NAME))
|
|
||||||
return false;
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean result) {
|
|
||||||
if (result) {
|
|
||||||
showRequest();
|
|
||||||
} else {
|
|
||||||
finish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -3,88 +3,14 @@ package com.topjohnwu.magisk.superuser;
|
|||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.os.Process;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.utils.SuConnector;
|
||||||
import com.topjohnwu.magisk.Data;
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.magisk.container.Policy;
|
|
||||||
import com.topjohnwu.magisk.container.SuLogEntry;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
|
|
||||||
import java.util.Date;
|
|
||||||
|
|
||||||
public class SuReceiver extends BroadcastReceiver {
|
public class SuReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
int fromUid, toUid, pid, mode;
|
if (intent != null)
|
||||||
String command, action;
|
SuConnector.handleLogs(intent, 1);
|
||||||
Policy policy;
|
|
||||||
|
|
||||||
MagiskManager mm = Data.MM();
|
|
||||||
|
|
||||||
if (intent == null) return;
|
|
||||||
|
|
||||||
mode = intent.getIntExtra("mode", -1);
|
|
||||||
if (mode < 0) return;
|
|
||||||
|
|
||||||
if (mode == Const.Value.NOTIFY_USER_TO_OWNER) {
|
|
||||||
Utils.toast(R.string.multiuser_hint_owner_request, Toast.LENGTH_LONG);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fromUid = intent.getIntExtra("from.uid", -1);
|
|
||||||
if (fromUid < 0) return;
|
|
||||||
if (fromUid == Process.myUid()) return; // Don't show anything if it's Magisk Manager
|
|
||||||
|
|
||||||
action = intent.getStringExtra("action");
|
|
||||||
if (action == null) return;
|
|
||||||
|
|
||||||
policy = mm.mDB.getPolicy(fromUid);
|
|
||||||
if (policy == null) {
|
|
||||||
try {
|
|
||||||
policy = new Policy(fromUid, mm.getPackageManager());
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SuLogEntry log = new SuLogEntry(policy);
|
|
||||||
|
|
||||||
String message;
|
|
||||||
switch (action) {
|
|
||||||
case "allow":
|
|
||||||
message = mm.getString(R.string.su_allow_toast, policy.appName);
|
|
||||||
log.action = true;
|
|
||||||
break;
|
|
||||||
case "deny":
|
|
||||||
message = mm.getString(R.string.su_deny_toast, policy.appName);
|
|
||||||
log.action = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (policy.notification && Data.suNotificationType == Const.Value.NOTIFICATION_TOAST)
|
|
||||||
Utils.toast(message, Toast.LENGTH_SHORT);
|
|
||||||
|
|
||||||
if (mode == Const.Value.NOTIFY_NORMAL_LOG && policy.logging) {
|
|
||||||
toUid = intent.getIntExtra("to.uid", -1);
|
|
||||||
if (toUid < 0) return;
|
|
||||||
pid = intent.getIntExtra("pid", -1);
|
|
||||||
if (pid < 0) return;
|
|
||||||
command = intent.getStringExtra("command");
|
|
||||||
if (command == null) return;
|
|
||||||
log.toUid = toUid;
|
|
||||||
log.fromPid = pid;
|
|
||||||
log.command = command;
|
|
||||||
log.date = new Date();
|
|
||||||
mm.mDB.addLog(log);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
import android.support.annotation.Keep;
|
|
||||||
|
|
||||||
import com.topjohnwu.utils.SignBoot;
|
import com.topjohnwu.utils.SignBoot;
|
||||||
|
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import androidx.annotation.Keep;
|
||||||
|
|
||||||
public class BootSigner {
|
public class BootSigner {
|
||||||
|
|
||||||
@Keep
|
@Keep
|
||||||
|
@@ -0,0 +1,113 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.androidnetworking.AndroidNetworking;
|
||||||
|
import com.androidnetworking.error.ANError;
|
||||||
|
import com.androidnetworking.interfaces.DownloadListener;
|
||||||
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.asyncs.PatchAPK;
|
||||||
|
import com.topjohnwu.magisk.components.ProgressNotification;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.utils.JarMap;
|
||||||
|
import com.topjohnwu.utils.SignAPK;
|
||||||
|
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
|
||||||
|
public class DlInstallManager {
|
||||||
|
|
||||||
|
public static void upgrade(String name) {
|
||||||
|
dlInstall(name, new PatchPackageName());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void restore() {
|
||||||
|
String name = Utils.fmt("MagiskManager v%s(%d)",
|
||||||
|
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
|
||||||
|
dlInstall(name, new RestoreManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void dlInstall(String name, ManagerDownloadListener listener) {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
File apk = new File(mm.getFilesDir(), "manager.apk");
|
||||||
|
ProgressNotification progress = new ProgressNotification(name);
|
||||||
|
listener.setInstances(apk, progress);
|
||||||
|
AndroidNetworking
|
||||||
|
.download(Data.managerLink, apk.getParent(), apk.getName())
|
||||||
|
.setExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
|
||||||
|
.build()
|
||||||
|
.setDownloadProgressListener(progress)
|
||||||
|
.startDownload(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract static class ManagerDownloadListener implements DownloadListener {
|
||||||
|
private File apk;
|
||||||
|
private ProgressNotification progress;
|
||||||
|
|
||||||
|
private void setInstances(File apk, ProgressNotification progress) {
|
||||||
|
this.apk = apk;
|
||||||
|
this.progress = progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void onDownloadComplete(File apk, ProgressNotification progress);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void onDownloadComplete() {
|
||||||
|
onDownloadComplete(apk, progress);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(ANError anError) {
|
||||||
|
progress.dlFail();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PatchPackageName extends ManagerDownloadListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloadComplete(File apk, ProgressNotification progress) {
|
||||||
|
File patched = apk;
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
if (!mm.getPackageName().equals(BuildConfig.APPLICATION_ID)) {
|
||||||
|
progress.getNotification()
|
||||||
|
.setProgress(0, 0, true)
|
||||||
|
.setContentTitle(mm.getString(R.string.hide_manager_title))
|
||||||
|
.setContentText("");
|
||||||
|
progress.update();
|
||||||
|
patched = new File(apk.getParent(), "patched.apk");
|
||||||
|
try {
|
||||||
|
JarMap jarMap = new JarMap(apk);
|
||||||
|
PatchAPK.patch(jarMap, mm.getPackageName());
|
||||||
|
SignAPK.sign(jarMap, new BufferedOutputStream(new FileOutputStream(patched)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
APKInstall.install(mm, patched);
|
||||||
|
progress.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class RestoreManager extends ManagerDownloadListener {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDownloadComplete(File apk, ProgressNotification progress) {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
progress.getNotification()
|
||||||
|
.setProgress(0, 0, true)
|
||||||
|
.setContentTitle(mm.getString(R.string.restore_img_msg))
|
||||||
|
.setContentText("");
|
||||||
|
progress.update();
|
||||||
|
Data.exportPrefs();
|
||||||
|
// Make it world readable
|
||||||
|
apk.setReadable(true, false);
|
||||||
|
if (ShellUtils.fastCmdResult("pm install " + apk))
|
||||||
|
RootUtils.rmAndLaunch(mm.getPackageName(), BuildConfig.APPLICATION_ID);
|
||||||
|
progress.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,17 +1,26 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
|
import android.app.Activity;
|
||||||
import android.app.KeyguardManager;
|
import android.app.KeyguardManager;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
import android.hardware.fingerprint.FingerprintManager;
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.CancellationSignal;
|
import android.os.CancellationSignal;
|
||||||
import android.security.keystore.KeyGenParameterSpec;
|
import android.security.keystore.KeyGenParameterSpec;
|
||||||
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
import android.security.keystore.KeyPermanentlyInvalidatedException;
|
||||||
import android.security.keystore.KeyProperties;
|
import android.security.keystore.KeyProperties;
|
||||||
|
import android.view.Gravity;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.components.CustomAlertDialog;
|
||||||
|
|
||||||
import java.security.KeyStore;
|
import java.security.KeyStore;
|
||||||
|
|
||||||
@@ -26,6 +35,16 @@ public abstract class FingerprintHelper {
|
|||||||
private Cipher cipher;
|
private Cipher cipher;
|
||||||
private CancellationSignal cancel;
|
private CancellationSignal cancel;
|
||||||
|
|
||||||
|
public static boolean useFingerPrint() {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
boolean fp = mm.mDB.getSettings(Const.Key.SU_FINGERPRINT, 0) != 0;
|
||||||
|
if (fp && !canUseFingerprint()) {
|
||||||
|
mm.mDB.setSettings(Const.Key.SU_FINGERPRINT, 0);
|
||||||
|
fp = false;
|
||||||
|
}
|
||||||
|
return fp;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean canUseFingerprint() {
|
public static boolean canUseFingerprint() {
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M)
|
||||||
return false;
|
return false;
|
||||||
@@ -35,6 +54,56 @@ public abstract class FingerprintHelper {
|
|||||||
return km.isKeyguardSecure() && fm != null && fm.isHardwareDetected() && fm.hasEnrolledFingerprints();
|
return km.isKeyguardSecure() && fm != null && fm.isHardwareDetected() && fm.hasEnrolledFingerprints();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void showAuthDialog(Activity activity, Runnable onSuccess) {
|
||||||
|
CustomAlertDialog dialog = new CustomAlertDialog(activity);
|
||||||
|
CustomAlertDialog.ViewHolder vh = dialog.getViewHolder();
|
||||||
|
try {
|
||||||
|
FingerprintHelper helper = new FingerprintHelper() {
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationError(int errorCode, CharSequence errString) {
|
||||||
|
vh.messageView.setTextColor(Color.RED);
|
||||||
|
vh.messageView.setText(errString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
|
||||||
|
vh.messageView.setTextColor(Color.RED);
|
||||||
|
vh.messageView.setText(helpString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationFailed() {
|
||||||
|
vh.messageView.setTextColor(Color.RED);
|
||||||
|
vh.messageView.setText(R.string.auth_fail);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
|
||||||
|
dialog.dismiss();
|
||||||
|
onSuccess.run();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Drawable fingerprint = activity.getResources().getDrawable(R.drawable.ic_fingerprint);
|
||||||
|
fingerprint.setBounds(0, 0, Utils.dpInPx(50), Utils.dpInPx(50));
|
||||||
|
Resources.Theme theme = activity.getTheme();
|
||||||
|
TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint});
|
||||||
|
fingerprint.setTint(ta.getColor(0, Color.GRAY));
|
||||||
|
ta.recycle();
|
||||||
|
vh.messageView.setCompoundDrawables(null, null, null, fingerprint);
|
||||||
|
vh.messageView.setCompoundDrawablePadding(Utils.dpInPx(20));
|
||||||
|
vh.messageView.setGravity(Gravity.CENTER);
|
||||||
|
dialog.setMessage(R.string.auth_fingerprint)
|
||||||
|
.setNegativeButton(R.string.close, (d, w) -> helper.cancel())
|
||||||
|
.setOnCancelListener(d -> helper.cancel())
|
||||||
|
.show();
|
||||||
|
helper.authenticate();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
protected FingerprintHelper() throws Exception {
|
protected FingerprintHelper() throws Exception {
|
||||||
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
|
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
|
||||||
manager = Data.MM().getSystemService(FingerprintManager.class);
|
manager = Data.MM().getSystemService(FingerprintManager.class);
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
import android.support.annotation.Keep;
|
|
||||||
|
|
||||||
public interface ISafetyNetHelper {
|
public interface ISafetyNetHelper {
|
||||||
|
|
||||||
int RESPONSE_ERR = 0x01;
|
int RESPONSE_ERR = 0x01;
|
||||||
@@ -10,14 +8,11 @@ public interface ISafetyNetHelper {
|
|||||||
int BASIC_PASS = 0x10;
|
int BASIC_PASS = 0x10;
|
||||||
int CTS_PASS = 0x20;
|
int CTS_PASS = 0x20;
|
||||||
|
|
||||||
@Keep
|
|
||||||
void attest();
|
void attest();
|
||||||
|
|
||||||
@Keep
|
|
||||||
int getVersion();
|
int getVersion();
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
@Keep
|
|
||||||
void onResponse(int responseCode);
|
void onResponse(int responseCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.utils;
|
|||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.support.annotation.StringRes;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
@@ -16,6 +15,8 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
public class LocaleManager {
|
public class LocaleManager {
|
||||||
public static Locale locale = Locale.getDefault();
|
public static Locale locale = Locale.getDefault();
|
||||||
public final static Locale defaultLocale = Locale.getDefault();
|
public final static Locale defaultLocale = Locale.getDefault();
|
||||||
|
@@ -1,86 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
|
||||||
|
|
||||||
import android.app.NotificationManager;
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.support.v4.app.NotificationCompat;
|
|
||||||
import android.support.v4.app.TaskStackBuilder;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
|
||||||
import com.topjohnwu.magisk.Data;
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.magisk.SplashActivity;
|
|
||||||
import com.topjohnwu.magisk.receivers.ManagerUpdate;
|
|
||||||
import com.topjohnwu.magisk.receivers.RebootReceiver;
|
|
||||||
|
|
||||||
public class NotificationMgr {
|
|
||||||
|
|
||||||
public static void magiskUpdate() {
|
|
||||||
MagiskManager mm = Data.MM();
|
|
||||||
|
|
||||||
Intent intent = new Intent(mm, SplashActivity.class);
|
|
||||||
intent.putExtra(Const.Key.OPEN_SECTION, "magisk");
|
|
||||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mm);
|
|
||||||
stackBuilder.addParentStack(SplashActivity.class);
|
|
||||||
stackBuilder.addNextIntent(intent);
|
|
||||||
PendingIntent pendingIntent = stackBuilder.getPendingIntent(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
|
||||||
builder.setSmallIcon(R.drawable.ic_magisk_outline)
|
|
||||||
.setContentTitle(mm.getString(R.string.magisk_update_title))
|
|
||||||
.setContentText(mm.getString(R.string.magisk_update_available, Data.remoteMagiskVersionString))
|
|
||||||
.setVibrate(new long[]{0, 100, 100, 100})
|
|
||||||
.setAutoCancel(true)
|
|
||||||
.setContentIntent(pendingIntent);
|
|
||||||
|
|
||||||
NotificationManager notificationManager =
|
|
||||||
(NotificationManager) mm.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
notificationManager.notify(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID, builder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void managerUpdate() {
|
|
||||||
MagiskManager mm = Data.MM();
|
|
||||||
String filename = Utils.fmt("MagiskManager-v%s(%d).apk",
|
|
||||||
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
|
|
||||||
|
|
||||||
Intent intent = new Intent(mm, ManagerUpdate.class);
|
|
||||||
intent.putExtra(Const.Key.INTENT_SET_LINK, Data.managerLink);
|
|
||||||
intent.putExtra(Const.Key.INTENT_SET_FILENAME, filename);
|
|
||||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(mm,
|
|
||||||
Const.ID.APK_UPDATE_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
|
||||||
builder.setSmallIcon(R.drawable.ic_magisk_outline)
|
|
||||||
.setContentTitle(mm.getString(R.string.manager_update_title))
|
|
||||||
.setContentText(mm.getString(R.string.manager_download_install))
|
|
||||||
.setVibrate(new long[]{0, 100, 100, 100})
|
|
||||||
.setAutoCancel(true)
|
|
||||||
.setContentIntent(pendingIntent);
|
|
||||||
|
|
||||||
NotificationManager notificationManager =
|
|
||||||
(NotificationManager) mm.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
notificationManager.notify(Const.ID.APK_UPDATE_NOTIFICATION_ID, builder.build());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void dtboPatched() {
|
|
||||||
MagiskManager mm = Data.MM();
|
|
||||||
|
|
||||||
Intent intent = new Intent(mm, RebootReceiver.class);
|
|
||||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(mm,
|
|
||||||
Const.ID.DTBO_NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
|
|
||||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(mm, Const.ID.NOTIFICATION_CHANNEL);
|
|
||||||
builder.setSmallIcon(R.drawable.ic_magisk_outline)
|
|
||||||
.setContentTitle(mm.getString(R.string.dtbo_patched_title))
|
|
||||||
.setContentText(mm.getString(R.string.dtbo_patched_reboot))
|
|
||||||
.setVibrate(new long[]{0, 100, 100, 100})
|
|
||||||
.addAction(R.drawable.ic_refresh, mm.getString(R.string.reboot), pendingIntent);
|
|
||||||
|
|
||||||
NotificationManager notificationManager =
|
|
||||||
(NotificationManager) mm.getSystemService(Context.NOTIFICATION_SERVICE);
|
|
||||||
notificationManager.notify(Const.ID.DTBO_NOTIFICATION_ID, builder.build());
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,7 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
@@ -14,6 +13,8 @@ import com.topjohnwu.superuser.io.SuFile;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public class RootUtils extends Shell.Initializer {
|
public class RootUtils extends Shell.Initializer {
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -24,32 +25,27 @@ public class RootUtils extends Shell.Initializer {
|
|||||||
Shell.su("db_clean " + Const.USER_ID, "pm uninstall " + pkg).exec();
|
Shell.su("db_clean " + Const.USER_ID, "pm uninstall " + pkg).exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void rmAndLaunch(String rm, String launch) {
|
||||||
|
Shell.su(Utils.fmt("(rm_launch %d %s %s)&", Const.USER_ID, rm, launch)).exec();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onInit(Context context, @NonNull Shell shell) {
|
public boolean onInit(Context context, @NonNull Shell shell) {
|
||||||
Shell.Job job = shell.newJob();
|
Shell.Job job = shell.newJob();
|
||||||
if (shell.isRoot()) {
|
if (shell.isRoot()) {
|
||||||
InputStream magiskUtils = context.getResources().openRawResource(R.raw.util_functions);
|
if (!new SuFile("/sbin/.magisk").exists())
|
||||||
InputStream managerUtils = context.getResources().openRawResource(R.raw.utils);
|
job.add("ln -s /sbin/.core /sbin/.magisk");
|
||||||
job.add(magiskUtils).add(managerUtils);
|
|
||||||
|
|
||||||
|
job.add(context.getResources().openRawResource(R.raw.util_functions))
|
||||||
|
.add(context.getResources().openRawResource(R.raw.utils));
|
||||||
Const.MAGISK_DISABLE_FILE = new SuFile("/cache/.disable_magisk");
|
Const.MAGISK_DISABLE_FILE = new SuFile("/cache/.disable_magisk");
|
||||||
SuFile file = new SuFile("/sbin/.core/img");
|
|
||||||
if (file.exists()) {
|
|
||||||
Const.MAGISK_PATH = file;
|
|
||||||
} else if ((file = new SuFile("/dev/magisk/img")).exists()) {
|
|
||||||
Const.MAGISK_PATH = file;
|
|
||||||
} else {
|
|
||||||
Const.MAGISK_PATH = new SuFile("/magisk");
|
|
||||||
}
|
|
||||||
Const.MAGISK_HOST_FILE = new SuFile(Const.MAGISK_PATH + "/.core/hosts");
|
|
||||||
|
|
||||||
Data.loadMagiskInfo();
|
Data.loadMagiskInfo();
|
||||||
} else {
|
} else {
|
||||||
InputStream nonroot = context.getResources().openRawResource(R.raw.nonroot_utils);
|
InputStream nonroot = context.getResources().openRawResource(R.raw.nonroot_utils);
|
||||||
job.add(nonroot);
|
job.add(nonroot);
|
||||||
}
|
}
|
||||||
|
|
||||||
job.add("mount_partitions", "get_flags", "run_migrations").exec();
|
job.add("mount_partitions", "get_flags", "run_migrations", "export BOOTMODE=true").exec();
|
||||||
|
|
||||||
Data.keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
|
Data.keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
|
||||||
Data.keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT"));
|
Data.keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT"));
|
||||||
|
157
app/src/full/java/com/topjohnwu/magisk/utils/SuConnector.java
Normal file
157
app/src/full/java/com/topjohnwu/magisk/utils/SuConnector.java
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.net.LocalSocket;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Process;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.container.Policy;
|
||||||
|
import com.topjohnwu.magisk.container.SuLogEntry;
|
||||||
|
|
||||||
|
import java.io.BufferedInputStream;
|
||||||
|
import java.io.BufferedOutputStream;
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
public abstract class SuConnector {
|
||||||
|
|
||||||
|
protected LocalSocket socket = new LocalSocket();
|
||||||
|
protected DataOutputStream out;
|
||||||
|
protected DataInputStream in;
|
||||||
|
|
||||||
|
public SuConnector(String name) throws IOException {
|
||||||
|
connect(name);
|
||||||
|
out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
|
||||||
|
in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String readString() throws IOException {
|
||||||
|
int len = in.readInt();
|
||||||
|
byte[] buf = new byte[len];
|
||||||
|
in.readFully(buf);
|
||||||
|
return new String(buf, "UTF-8");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle readSocketInput() throws IOException {
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
while (true) {
|
||||||
|
String name = readString();
|
||||||
|
if (TextUtils.equals(name, "eof"))
|
||||||
|
break;
|
||||||
|
bundle.putString(name, readString());
|
||||||
|
}
|
||||||
|
return bundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void response() {
|
||||||
|
try {
|
||||||
|
onResponse();
|
||||||
|
out.flush();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
in.close();
|
||||||
|
out.close();
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException ignored) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void connect(String name) throws IOException;
|
||||||
|
|
||||||
|
protected abstract void onResponse() throws IOException;
|
||||||
|
|
||||||
|
public static void handleLogs(Intent intent, int version) {
|
||||||
|
|
||||||
|
int fromUid = intent.getIntExtra("from.uid", -1);
|
||||||
|
if (fromUid < 0) return;
|
||||||
|
if (fromUid == Process.myUid()) return;
|
||||||
|
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
PackageManager pm = mm.getPackageManager();
|
||||||
|
Policy policy;
|
||||||
|
|
||||||
|
boolean notify;
|
||||||
|
Bundle data = intent.getExtras();
|
||||||
|
if (data.containsKey("notify")) {
|
||||||
|
notify = data.getBoolean("notify");
|
||||||
|
try {
|
||||||
|
policy = new Policy(fromUid, pm);
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Doesn't report whether notify or not, check database ourselves
|
||||||
|
policy = mm.mDB.getPolicy(fromUid);
|
||||||
|
if (policy == null)
|
||||||
|
return;
|
||||||
|
notify = policy.notification;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (version == 1) {
|
||||||
|
String action = intent.getStringExtra("action");
|
||||||
|
if (action == null) return;
|
||||||
|
switch (action) {
|
||||||
|
case "allow":
|
||||||
|
policy.policy = Policy.ALLOW;
|
||||||
|
break;
|
||||||
|
case "deny":
|
||||||
|
policy.policy = Policy.DENY;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
policy.policy = data.getInt("policy", -1);
|
||||||
|
if (policy.policy < 0)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notify)
|
||||||
|
handleNotify(policy);
|
||||||
|
|
||||||
|
SuLogEntry log = new SuLogEntry(policy);
|
||||||
|
|
||||||
|
int toUid = intent.getIntExtra("to.uid", -1);
|
||||||
|
if (toUid < 0) return;
|
||||||
|
int pid = intent.getIntExtra("pid", -1);
|
||||||
|
if (pid < 0) return;
|
||||||
|
String command = intent.getStringExtra("command");
|
||||||
|
if (command == null) return;
|
||||||
|
log.toUid = toUid;
|
||||||
|
log.fromPid = pid;
|
||||||
|
log.command = command;
|
||||||
|
log.date = new Date();
|
||||||
|
mm.mDB.addLog(log);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void handleNotify(Policy policy) {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
String message = mm.getString(policy.policy == Policy.ALLOW ?
|
||||||
|
R.string.su_allow_toast : R.string.su_deny_toast, policy.appName);
|
||||||
|
if (policy.notification && Data.suNotificationType == Const.Value.NOTIFICATION_TOAST)
|
||||||
|
Utils.toast(message, Toast.LENGTH_SHORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handleNotify(Intent intent) {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
int fromUid = intent.getIntExtra("from.uid", -1);
|
||||||
|
if (fromUid < 0) return;
|
||||||
|
if (fromUid == Process.myUid()) return;
|
||||||
|
try {
|
||||||
|
Policy policy = new Policy(fromUid, mm.getPackageManager());
|
||||||
|
policy.policy = intent.getIntExtra("policy", -1);
|
||||||
|
if (policy.policy >= 0)
|
||||||
|
handleNotify(policy);
|
||||||
|
} catch (PackageManager.NameNotFoundException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
@@ -1,7 +1,5 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
import android.support.annotation.IntDef;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
@@ -9,23 +7,24 @@ import java.lang.annotation.RetentionPolicy;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import androidx.annotation.IntDef;
|
||||||
|
|
||||||
public class Topic {
|
public class Topic {
|
||||||
|
|
||||||
public static final int MAGISK_HIDE_DONE = 0;
|
public static final int MAGISK_HIDE_DONE = 0;
|
||||||
public static final int RELOAD_ACTIVITY = 1;
|
public static final int MODULE_LOAD_DONE = 1;
|
||||||
public static final int MODULE_LOAD_DONE = 2;
|
public static final int REPO_LOAD_DONE = 2;
|
||||||
public static final int REPO_LOAD_DONE = 3;
|
public static final int UPDATE_CHECK_DONE = 3;
|
||||||
public static final int UPDATE_CHECK_DONE = 4;
|
public static final int SNET_CHECK_DONE = 4;
|
||||||
public static final int SNET_CHECK_DONE = 5;
|
public static final int LOCALE_FETCH_DONE = 5;
|
||||||
public static final int LOCALE_FETCH_DONE = 6;
|
|
||||||
|
|
||||||
@IntDef({MAGISK_HIDE_DONE, RELOAD_ACTIVITY, MODULE_LOAD_DONE, REPO_LOAD_DONE,
|
@IntDef({MAGISK_HIDE_DONE, MODULE_LOAD_DONE, REPO_LOAD_DONE,
|
||||||
UPDATE_CHECK_DONE, SNET_CHECK_DONE, LOCALE_FETCH_DONE})
|
UPDATE_CHECK_DONE, SNET_CHECK_DONE, LOCALE_FETCH_DONE})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface TopicID {}
|
public @interface TopicID {}
|
||||||
|
|
||||||
// We will not dynamically add topics, so use arrays instead of hash tables
|
// We will not dynamically add topics, so use arrays instead of hash tables
|
||||||
private static Store[] topicList = new Store[7];
|
private static Store[] topicList = new Store[6];
|
||||||
|
|
||||||
public static void subscribe(Subscriber sub, @TopicID int... topics) {
|
public static void subscribe(Subscriber sub, @TopicID int... topics) {
|
||||||
for (int topic : topics) {
|
for (int topic : topics) {
|
||||||
|
@@ -6,12 +6,17 @@ import android.content.ComponentName;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.Resources;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.provider.OpenableColumns;
|
import android.provider.OpenableColumns;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.androidnetworking.AndroidNetworking;
|
||||||
import com.topjohnwu.magisk.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.Data;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
@@ -19,6 +24,7 @@ import com.topjohnwu.magisk.R;
|
|||||||
import com.topjohnwu.magisk.container.Module;
|
import com.topjohnwu.magisk.container.Module;
|
||||||
import com.topjohnwu.magisk.container.ValueSortedMap;
|
import com.topjohnwu.magisk.container.ValueSortedMap;
|
||||||
import com.topjohnwu.magisk.services.UpdateCheckService;
|
import com.topjohnwu.magisk.services.UpdateCheckService;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
@@ -77,7 +83,7 @@ public class Utils {
|
|||||||
if (mm.prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) {
|
if (mm.prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) {
|
||||||
if (scheduler.getAllPendingJobs().isEmpty() ||
|
if (scheduler.getAllPendingJobs().isEmpty() ||
|
||||||
Const.UPDATE_SERVICE_VER > mm.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
|
Const.UPDATE_SERVICE_VER > mm.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
|
||||||
ComponentName service = new ComponentName(mm, UpdateCheckService.class);
|
ComponentName service = new ComponentName(mm, Data.classMap.get(UpdateCheckService.class));
|
||||||
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
|
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
|
||||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
||||||
.setPersisted(true)
|
.setPersisted(true)
|
||||||
@@ -113,13 +119,40 @@ public class Utils {
|
|||||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
Map<String, Module> moduleMap = new ValueSortedMap<>();
|
Map<String, Module> moduleMap = new ValueSortedMap<>();
|
||||||
SuFile path = new SuFile(Const.MAGISK_PATH);
|
SuFile path = new SuFile(Const.MAGISK_PATH);
|
||||||
String[] modules = path.list(
|
SuFile[] modules = path.listFiles(
|
||||||
(file, name) -> !name.equals("lost+found") && !name.equals(".core"));
|
(file, name) -> !name.equals("lost+found") && !name.equals(".core"));
|
||||||
for (String name : modules) {
|
for (SuFile file : modules) {
|
||||||
Module module = new Module(Const.MAGISK_PATH + "/" + name);
|
if (file.isFile()) continue;
|
||||||
|
Module module = new Module(Const.MAGISK_PATH + "/" + file.getName());
|
||||||
moduleMap.put(module.getId(), module);
|
moduleMap.put(module.getId(), module);
|
||||||
}
|
}
|
||||||
Topic.publish(Topic.MODULE_LOAD_DONE, moduleMap);
|
Topic.publish(Topic.MODULE_LOAD_DONE, moduleMap);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getAppLabel(ApplicationInfo info, PackageManager pm) {
|
||||||
|
try {
|
||||||
|
if (info.labelRes > 0) {
|
||||||
|
Resources res = pm.getResourcesForApplication(info);
|
||||||
|
Configuration config = new Configuration();
|
||||||
|
config.setLocale(LocaleManager.locale);
|
||||||
|
res.updateConfiguration(config, res.getDisplayMetrics());
|
||||||
|
return res.getString(info.labelRes);
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
return info.loadLabel(pm).toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean showSuperUser() {
|
||||||
|
if (Data.multiuserState < 0)
|
||||||
|
Data.multiuserState = Data.MM().mDB.getSettings(Const.Key.SU_MULTIUSER_MODE,
|
||||||
|
Const.Value.MULTIUSER_MODE_OWNER_ONLY);
|
||||||
|
return Shell.rootAccess() && (Const.USER_ID == 0 ||
|
||||||
|
Data.multiuserState != Const.Value.MULTIUSER_MODE_OWNER_MANAGED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String dlString(String url) {
|
||||||
|
String s = (String) AndroidNetworking.get(url).build().executeForString().getResult();
|
||||||
|
return s == null ? "" : s;
|
||||||
|
}
|
||||||
}
|
}
|
@@ -20,7 +20,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
@@ -54,42 +54,42 @@
|
|||||||
android:textAppearance="@style/TextAppearance.AppCompat.Headline"/>
|
android:textAppearance="@style/TextAppearance.AppCompat.Headline"/>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<com.topjohnwu.magisk.components.AboutCardRow
|
<a.l
|
||||||
android:id="@+id/app_version_info"
|
android:id="@+id/app_version_info"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:icon="@drawable/ic_info_outline"
|
app:icon="@drawable/ic_info_outline"
|
||||||
app:text="@string/app_version"/>
|
app:text="@string/app_version"/>
|
||||||
|
|
||||||
<com.topjohnwu.magisk.components.AboutCardRow
|
<a.l
|
||||||
android:id="@+id/app_changelog"
|
android:id="@+id/app_changelog"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:icon="@drawable/ic_history"
|
app:icon="@drawable/ic_history"
|
||||||
app:text="@string/app_changelog"/>
|
app:text="@string/app_changelog"/>
|
||||||
|
|
||||||
<com.topjohnwu.magisk.components.AboutCardRow
|
<a.l
|
||||||
android:id="@+id/app_translators"
|
android:id="@+id/app_translators"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:icon="@drawable/ic_language"
|
app:icon="@drawable/ic_language"
|
||||||
app:text="@string/app_translators"/>
|
app:text="@string/app_translators"/>
|
||||||
|
|
||||||
<com.topjohnwu.magisk.components.AboutCardRow
|
<a.l
|
||||||
android:id="@+id/follow_twitter"
|
android:id="@+id/follow_twitter"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:icon="@drawable/ic_twitter"
|
app:icon="@drawable/ic_twitter"
|
||||||
app:text="@string/follow_twitter"/>
|
app:text="@string/follow_twitter"/>
|
||||||
|
|
||||||
<com.topjohnwu.magisk.components.AboutCardRow
|
<a.l
|
||||||
android:id="@+id/app_source_code"
|
android:id="@+id/app_source_code"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:icon="@drawable/ic_github"
|
app:icon="@drawable/ic_github"
|
||||||
app:text="@string/app_source_code"/>
|
app:text="@string/app_source_code"/>
|
||||||
|
|
||||||
<com.topjohnwu.magisk.components.AboutCardRow
|
<a.l
|
||||||
android:id="@+id/support_thread"
|
android:id="@+id/support_thread"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -98,7 +98,7 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@@ -20,7 +20,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:layout_width="fill_parent"
|
android:layout_width="fill_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
@@ -40,14 +40,14 @@
|
|||||||
android:text="@string/donation"
|
android:text="@string/donation"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Headline"/>
|
android:textAppearance="@style/TextAppearance.AppCompat.Headline"/>
|
||||||
|
|
||||||
<com.topjohnwu.magisk.components.AboutCardRow
|
<a.l
|
||||||
android:id="@+id/paypal"
|
android:id="@+id/paypal"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
app:icon="@drawable/ic_paypal"
|
app:icon="@drawable/ic_paypal"
|
||||||
app:text="PayPal"/>
|
app:text="PayPal"/>
|
||||||
|
|
||||||
<com.topjohnwu.magisk.components.AboutCardRow
|
<a.l
|
||||||
android:id="@+id/patreon"
|
android:id="@+id/patreon"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
|
@@ -42,7 +42,7 @@
|
|||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/no_thanks"
|
android:id="@+id/close"
|
||||||
style="?android:borderlessButtonStyle"
|
style="?android:borderlessButtonStyle"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="50dp"
|
android:layout_height="50dp"
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/drawer_layout"
|
android:id="@+id/drawer_layout"
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<android.support.design.widget.NavigationView
|
<com.google.android.material.navigation.NavigationView
|
||||||
android:id="@+id/nav_view"
|
android:id="@+id/nav_view"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@@ -32,4 +32,4 @@
|
|||||||
android:fitsSystemWindows="true"
|
android:fitsSystemWindows="true"
|
||||||
app:menu="@menu/drawer" />
|
app:menu="@menu/drawer" />
|
||||||
|
|
||||||
</android.support.v4.widget.DrawerLayout>
|
</androidx.drawerlayout.widget.DrawerLayout>
|
||||||
|
@@ -3,7 +3,7 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:id="@+id/su_popup"
|
android:id="@+id/su_popup"
|
||||||
tools:context=".superuser.RequestActivity"
|
tools:context=".SuRequestActivity"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:minWidth="350dp"
|
android:minWidth="350dp"
|
||||||
|
@@ -7,7 +7,7 @@
|
|||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context="com.topjohnwu.magisk.fragments.LogFragment">
|
tools:context="com.topjohnwu.magisk.fragments.LogFragment">
|
||||||
|
|
||||||
<android.support.design.widget.TabLayout
|
<com.google.android.material.tabs.TabLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="?attr/colorPrimary"
|
android:background="?attr/colorPrimary"
|
||||||
@@ -17,10 +17,10 @@
|
|||||||
android:elevation="4dp"
|
android:elevation="4dp"
|
||||||
android:visibility="gone">
|
android:visibility="gone">
|
||||||
|
|
||||||
</android.support.design.widget.TabLayout>
|
</com.google.android.material.tabs.TabLayout>
|
||||||
|
|
||||||
|
|
||||||
<android.support.v4.view.ViewPager
|
<androidx.viewpager.widget.ViewPager
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:id="@+id/container"/>
|
android:id="@+id/container"/>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.v4.widget.SwipeRefreshLayout
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/swipeRefreshLayout"
|
android:id="@+id/swipeRefreshLayout"
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/core_only_notice"
|
android:id="@+id/core_only_notice"
|
||||||
style="?attr/cardStyle"
|
style="?attr/cardStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -55,9 +55,9 @@
|
|||||||
android:text="@string/settings_core_only_title"
|
android:text="@string/settings_core_only_title"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
</android.support.v7.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
style="?attr/cardStyle"
|
style="?attr/cardStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@@ -141,9 +141,9 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/safetyNet_card"
|
android:id="@+id/safetyNet_card"
|
||||||
style="?attr/cardStyle"
|
style="?attr/cardStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -259,9 +259,9 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/install_option_card"
|
android:id="@+id/install_option_card"
|
||||||
style="?attr/cardStyle"
|
style="?attr/cardStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -308,9 +308,9 @@
|
|||||||
android:text="@string/keep_dm_verity" />
|
android:text="@string/keep_dm_verity" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</android.support.v7.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/install_button"
|
android:id="@+id/install_button"
|
||||||
style="?attr/cardStyle"
|
style="?attr/cardStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -361,9 +361,9 @@
|
|||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
<android.support.v7.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
android:id="@+id/uninstall_button"
|
android:id="@+id/uninstall_button"
|
||||||
style="?attr/cardStyle"
|
style="?attr/cardStyle"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -392,10 +392,10 @@
|
|||||||
android:textSize="20sp"
|
android:textSize="20sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
</android.support.v4.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user