You've already forked Magisk
mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-09-06 06:36:58 +00:00
Compare commits
241 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
0543239cca | ||
|
|
ff3dad2457 | ||
|
|
298d5e197b | ||
|
|
d73c0a998d | ||
|
|
1b79a3ddbf | ||
|
|
a8478ace18 | ||
|
|
72cf5f3f9f | ||
|
|
6f9d493a18 | ||
|
|
08f7d5ebff | ||
|
|
1fe3675403 | ||
|
|
a0f956d2c1 | ||
|
|
1560f91b4a | ||
|
|
c20f362594 | ||
|
|
7ae8c26e50 | ||
|
|
adfffe6121 | ||
|
|
64601baa76 | ||
|
|
aa374b51f1 | ||
|
|
5c483745ff | ||
|
|
0c247110a0 | ||
|
|
1643638a78 | ||
|
|
4ace228fc2 | ||
|
|
25aa86a0dc | ||
|
|
70d3b24338 | ||
|
|
8664e9d19b | ||
|
|
50d9877446 | ||
|
|
fe06352089 | ||
|
|
7b599419b5 | ||
|
|
491adf072e | ||
|
|
f6aae2b048 | ||
|
|
d2d5c94633 | ||
|
|
10581f9ef2 | ||
|
|
c7e0e1c038 | ||
|
|
a914d701eb | ||
|
|
0f9dee6e9c | ||
|
|
aa383e2190 | ||
|
|
9bbfcf326c | ||
|
|
3948e67c8f | ||
|
|
d56e1b2cc5 | ||
|
|
bfac1f1bc2 | ||
|
|
d4a956c355 | ||
|
|
6c71fefa58 | ||
|
|
ad3003c00a | ||
|
|
0ad5dcb258 | ||
|
|
d790309b02 | ||
|
|
1072faf309 | ||
|
|
d2c196896d | ||
|
|
e42b608444 | ||
|
|
89a501a3af | ||
|
|
c19b78180c | ||
|
|
c0b750a09a | ||
|
|
c967e618a1 | ||
|
|
59f78d7dfc | ||
|
|
d8405f0d05 | ||
|
|
0f34f0033c | ||
|
|
190646d50c | ||
|
|
a46c6252c6 | ||
|
|
5c1886c8f5 | ||
|
|
afcb3d8f34 | ||
|
|
9fbffafdbf | ||
|
|
075f0458f7 | ||
|
|
d4568aa0a7 | ||
|
|
97588408a2 | ||
|
|
1def9b301b | ||
|
|
5bac442b18 | ||
|
|
6add682705 | ||
|
|
8b50d84a05 | ||
|
|
d3858b81e2 | ||
|
|
bdff9769be | ||
|
|
c61df75e5e | ||
|
|
a74bf2cc27 | ||
|
|
ada0f93686 | ||
|
|
ff36f2ba17 | ||
|
|
5164cfd399 | ||
|
|
5fa021503e | ||
|
|
7b5d79d313 | ||
|
|
3e3f38500d | ||
|
|
5109b9abfd | ||
|
|
7fb4777c1c | ||
|
|
c38533e0f8 | ||
|
|
51ba99d09e | ||
|
|
9159f86a9e | ||
|
|
e139f4fc13 | ||
|
|
2fbfeacb87 | ||
|
|
ebb7a9fcda | ||
|
|
9e72317302 | ||
|
|
d764c20c08 | ||
|
|
9c17b8a098 | ||
|
|
3084873154 | ||
|
|
32809e56d0 | ||
|
|
9f05b182a2 | ||
|
|
525484e834 | ||
|
|
65a4e69cae | ||
|
|
e973f8bab9 | ||
|
|
92466671ff | ||
|
|
6d61106070 | ||
|
|
ac13749fb8 | ||
|
|
7ec1a9a316 | ||
|
|
cf17e21ad3 | ||
|
|
0e0240c4ab | ||
|
|
d1b290b91a | ||
|
|
a63696836c | ||
|
|
46aad00f16 | ||
|
|
252afe8932 | ||
|
|
9dd467a613 | ||
|
|
4c14df67cc | ||
|
|
20e0fe3ba1 | ||
|
|
6a005135f2 | ||
|
|
82e8375957 | ||
|
|
bb25edc09e | ||
|
|
169c0fe4af | ||
|
|
cd6918e6eb | ||
|
|
5be035fd44 | ||
|
|
f1edc8443c | ||
|
|
d9564bd04c | ||
|
|
35f1c396f2 | ||
|
|
6acb950990 | ||
|
|
27e0d1641a | ||
|
|
9ac71ff8af | ||
|
|
075737a4ec | ||
|
|
6d0e4a6a5e | ||
|
|
a2544768a0 | ||
|
|
8574a14ed2 | ||
|
|
e90c555c18 | ||
|
|
863b9a410f | ||
|
|
23c7bbc7d5 | ||
|
|
f900189f90 | ||
|
|
7c74be2790 | ||
|
|
70dd2d4829 | ||
|
|
914b7ee056 | ||
|
|
e39f83edbf | ||
|
|
52fe0c6abb | ||
|
|
5cb3e5937f | ||
|
|
e0cd224831 | ||
|
|
de225ac64a | ||
|
|
5807808a10 | ||
|
|
362877d18f | ||
|
|
88b8dd0149 | ||
|
|
1552f32e09 | ||
|
|
50b73a6720 | ||
|
|
53e51f1735 | ||
|
|
40b63bfebe | ||
|
|
89861eceef | ||
|
|
b8eaff66fa | ||
|
|
a747fdd27d | ||
|
|
27851bdefa | ||
|
|
3fdeb40ddf | ||
|
|
546c7cebd3 | ||
|
|
473902f5f4 | ||
|
|
41c0721159 | ||
|
|
413d4badfd | ||
|
|
c5d67ebf72 | ||
|
|
91818cfa1a | ||
|
|
6263d684d9 | ||
|
|
07140d33a7 | ||
|
|
4ffc388491 | ||
|
|
0ef026c610 |
65
README.MD
65
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,18 +17,18 @@
|
|||||||
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 tested on macOS, Ubuntu, and Windows 10 using the latest stable NDK and NDK r10e. Officially released binaries were built with NDK r10e.
|
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 `--release` flag), you need a Java Keystore file `release-key.jks` to sign Magisk Manager's APK. 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. The SafetyNet extension pack requires the full Magisk Manager as a `compileOnly` dependency. Build the **release** APK, convert it back to Java `.class` files (I use [dex2jar](https://github.com/pxb1988/dex2jar)), and place the converted JAR under `snet/libs` before compiling.
|
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).
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
```
|
```
|
||||||
Magisk, including all git submodules are free software:
|
Magisk, including all git submodules are free software:
|
||||||
you can redistribute it and/or modify it under the terms of the
|
you can redistribute it and/or modify it under the terms of the
|
||||||
GNU General Public License as published by the Free Software Foundation,
|
GNU General Public License as published by the Free Software Foundation,
|
||||||
either version 3 of the License, or (at your option) any later version.
|
either version 3 of the License, or (at your option) any later version.
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful,
|
This program is distributed in the hope that it will be useful,
|
||||||
@@ -31,44 +39,3 @@ GNU General Public License for more details.
|
|||||||
You should have received a copy of the GNU General Public License
|
You should have received a copy of the GNU General Public License
|
||||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
```
|
```
|
||||||
|
|
||||||
## Credits
|
|
||||||
|
|
||||||
**MagiskManager** (`app`)
|
|
||||||
|
|
||||||
* Copyright 2016-2018, John Wu (@topjohnwu)
|
|
||||||
* All contributors and translators on Github
|
|
||||||
|
|
||||||
**MagiskSU** (`native/jni/su`)
|
|
||||||
|
|
||||||
* Copyright 2016-2018, John Wu (@topjohnwu)
|
|
||||||
* Copyright 2015, Pierre-Hugues Husson (phh@phh.me)
|
|
||||||
* Copyright 2013, Koushik Dutta (@koush)
|
|
||||||
* Copyright 2010, Adam Shanks (@ChainsDD)
|
|
||||||
* Copyright 2008, Zinx Verituse (@zinxv)
|
|
||||||
|
|
||||||
**MagiskPolicy** (`native/jni/magiskpolicy`)
|
|
||||||
|
|
||||||
* Copyright 2016-2018, John Wu (@topjohnwu)
|
|
||||||
* Copyright 2015, Pierre-Hugues Husson (phh@phh.me)
|
|
||||||
* Copyright 2015, Joshua Brindle (@joshua_brindle)
|
|
||||||
|
|
||||||
**MagiskHide** (`native/jni/magiskhide`)
|
|
||||||
|
|
||||||
* Copyright 2016-2018, John Wu (@topjohnwu)
|
|
||||||
* Copyright 2016, Pierre-Hugues Husson (phh@phh.me)
|
|
||||||
|
|
||||||
**resetprop** (`native/jni/resetprop`)
|
|
||||||
|
|
||||||
* Copyright 2016-2018 John Wu (@topjohnwu)
|
|
||||||
* Copyright 2016 nkk71 (nkk71x@gmail.com)
|
|
||||||
|
|
||||||
**External Dependencies** (`native/jni/external`)
|
|
||||||
|
|
||||||
* Makefile for busybox, generated by [ndk-busybox-kitchen](https://github.com/topjohnwu/ndk-busybox-kitchen)
|
|
||||||
* Each dependencies has its own license/copyright information in each subdirectory.
|
|
||||||
All of them are either GPL or GPL compatible.
|
|
||||||
|
|
||||||
**Others Not Mentioned**
|
|
||||||
|
|
||||||
* Copyright 2016-2018, John Wu (@topjohnwu)
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Magisk Manager
|
# Magisk Manager
|
||||||
This repo is no longer an independent component. It is a submodule of the [Magisk Project](https://github.com/topjohnwu/Magisk).
|
This repo is no longer an independent component. It is merged into the [Magisk Project](https://github.com/topjohnwu/Magisk).
|
||||||
|
|
||||||
# Translations
|
# Translations
|
||||||
The default (English) string resources are scattered in these files: `src/full/res/values/strings.xml`, `src/main/res/values/strings.xml`, `src/stub/res/values/strings.xml`.
|
The default (English) strings are mainly in `src/full/res/values/strings.xml`; some are scattered in `src/main/res/values/strings.xml` and `src/stub/res/values/strings.xml`.
|
||||||
Place the translated XMLs in the corresponding folder to the locale.
|
Translations are highly appreciated via pull requests here on Github.
|
||||||
Translations are highly appreciated via pull requests here on Github.
|
Place translated XMLs in the corresponding locale folder.
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
|
def configProps = new Properties()
|
||||||
|
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
|
||||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||||
@@ -8,18 +12,28 @@ android {
|
|||||||
applicationId "com.topjohnwu.magisk"
|
applicationId "com.topjohnwu.magisk"
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
targetSdkVersion rootProject.ext.compileSdkVersion
|
targetSdkVersion rootProject.ext.compileSdkVersion
|
||||||
javaCompileOptions {
|
}
|
||||||
annotationProcessorOptions {
|
|
||||||
argument('butterknife.debuggable', 'false')
|
signingConfigs {
|
||||||
}
|
config {
|
||||||
|
storeFile rootProject.file('release-key.jks')
|
||||||
|
storePassword configProps['keyStorePass']
|
||||||
|
keyAlias configProps['keyAlias']
|
||||||
|
keyPassword configProps['keyPass']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
debug {
|
||||||
|
// If keystore exists, sign the APK with custom signature
|
||||||
|
if (signingConfigs.config.storeFile.exists())
|
||||||
|
signingConfig signingConfigs.config
|
||||||
|
}
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
shrinkResources true
|
shrinkResources true
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
signingConfig signingConfigs.config
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,8 +41,13 @@ android {
|
|||||||
|
|
||||||
productFlavors {
|
productFlavors {
|
||||||
full {
|
full {
|
||||||
versionCode 129
|
versionName configProps['appVersion']
|
||||||
versionName "5.8.3"
|
versionCode configProps['appVersionCode'] as Integer
|
||||||
|
javaCompileOptions {
|
||||||
|
annotationProcessorOptions {
|
||||||
|
argument('butterknife.debuggable', 'false')
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
stub {
|
stub {
|
||||||
versionCode 1
|
versionCode 1
|
||||||
@@ -52,14 +71,20 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
fullImplementation project(':utils')
|
fullImplementation project(':utils')
|
||||||
implementation "com.android.support:support-core-utils:${rootProject.ext.supportLibVersion}"
|
implementation "androidx.core:core:${rootProject.ext.androidXVersion}"
|
||||||
fullImplementation "com.android.support:preference-v7:${rootProject.ext.supportLibVersion}"
|
fullImplementation "androidx.preference:preference:${rootProject.ext.androidXVersion}"
|
||||||
fullImplementation "com.android.support:recyclerview-v7:${rootProject.ext.supportLibVersion}"
|
fullImplementation "androidx.recyclerview:recyclerview:${rootProject.ext.androidXVersion}"
|
||||||
fullImplementation "com.android.support:cardview-v7:${rootProject.ext.supportLibVersion}"
|
fullImplementation "androidx.cardview:cardview:${rootProject.ext.androidXVersion}"
|
||||||
fullImplementation "com.android.support:design:${rootProject.ext.supportLibVersion}"
|
fullImplementation "com.google.android.material:material:${rootProject.ext.androidXVersion}"
|
||||||
fullImplementation 'com.github.topjohnwu:libsu:1.3.0'
|
fullImplementation 'com.github.topjohnwu:libsu:2.0.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'
|
|
||||||
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
|
def butterKnifeVersion = '9.0.0-rc1'
|
||||||
|
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}"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
app/proguard-rules.pro
vendored
16
app/proguard-rules.pro
vendored
@@ -16,14 +16,20 @@
|
|||||||
# public *;
|
# public *;
|
||||||
#}
|
#}
|
||||||
|
|
||||||
# Keep all names, we are open source anyway :)
|
|
||||||
-keepnames class ** { *; }
|
|
||||||
|
|
||||||
# BouncyCastle
|
# BouncyCastle
|
||||||
-keep class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; }
|
-keep class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; }
|
||||||
-keep class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; }
|
-keep class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; }
|
||||||
-keep class org.bouncycastle.jcajce.provider.digest.SHA1** { *; }
|
-keep 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 { *; }
|
||||||
|
|
||||||
|
# Strip logging
|
||||||
|
-assumenosideeffects class com.topjohnwu.magisk.utils.Logger {
|
||||||
|
public *** debug(...);
|
||||||
|
}
|
||||||
|
|
||||||
|
# Excessive obfuscation
|
||||||
|
-repackageclasses 'a'
|
||||||
|
-allowaccessmodification
|
||||||
|
|||||||
@@ -1,21 +1,26 @@
|
|||||||
<?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" />
|
||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
|
||||||
|
<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">
|
||||||
@@ -25,57 +30,75 @@
|
|||||||
</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=".SettingsActivity"
|
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.p"
|
||||||
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>
|
||||||
<receiver android:name=".receivers.PackageReceiver">
|
<receiver android:name="a.i">
|
||||||
<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.j" />
|
||||||
<receiver android:name=".receivers.RebootReceiver" />
|
<receiver android:name="a.k" />
|
||||||
<receiver android:name=".receivers.ShortcutReceiver">
|
<receiver android:name="a.l">
|
||||||
<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 android:name=".services.OnBootIntentService" />
|
<!-- Service -->
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".services.UpdateCheckService"
|
android:name="a.m"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||||
|
<service
|
||||||
|
android:name="a.n"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:permission="android.permission.BIND_JOB_SERVICE" />
|
android:permission="android.permission.BIND_JOB_SERVICE" />
|
||||||
|
|
||||||
<!-- Hardcode GMS version -->
|
<!-- Hardcode GMS version -->
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.gms.version"
|
android:name="com.google.android.gms.version"
|
||||||
android:value="7095000" />
|
android:value="12451000" />
|
||||||
|
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
|||||||
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/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.BootReceiver;
|
||||||
|
|
||||||
|
public class h extends BootReceiver {
|
||||||
|
/* 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.PackageReceiver;
|
||||||
|
|
||||||
|
public class i extends PackageReceiver {
|
||||||
|
/* 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.receivers.ManagerUpdate;
|
||||||
|
|
||||||
|
public class j extends ManagerUpdate {
|
||||||
|
/* 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.receivers.RebootReceiver;
|
||||||
|
|
||||||
|
public class k extends RebootReceiver {
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
7
app/src/full/java/a/l.java
Normal file
7
app/src/full/java/a/l.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
|
||||||
|
|
||||||
|
public class l extends ShortcutReceiver {
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
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.services.OnBootService;
|
||||||
|
|
||||||
|
public class m extends OnBootService {
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
7
app/src/full/java/a/n.java
Normal file
7
app/src/full/java/a/n.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.services.UpdateCheckService;
|
||||||
|
|
||||||
|
public class n extends UpdateCheckService {
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
22
app/src/full/java/a/o.java
Normal file
22
app/src/full/java/a/o.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 o extends AboutCardRow {
|
||||||
|
/* stub */
|
||||||
|
|
||||||
|
public o(Context context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public o(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public o(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
}
|
||||||
|
}
|
||||||
7
app/src/full/java/a/p.java
Normal file
7
app/src/full/java/a/p.java
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package a;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.SuRequestActivity;
|
||||||
|
|
||||||
|
public class p 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 */
|
||||||
|
}
|
||||||
@@ -1,25 +1,23 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
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;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
||||||
import com.topjohnwu.magisk.components.AboutCardRow;
|
import com.topjohnwu.magisk.components.AboutCardRow;
|
||||||
import com.topjohnwu.magisk.components.Activity;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
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 Activity {
|
public class AboutActivity extends BaseActivity {
|
||||||
|
|
||||||
@BindView(R.id.toolbar) Toolbar toolbar;
|
@BindView(R.id.toolbar) Toolbar toolbar;
|
||||||
@BindView(R.id.app_version_info) AboutCardRow appVersionInfo;
|
@BindView(R.id.app_version_info) AboutCardRow appVersionInfo;
|
||||||
@@ -27,7 +25,7 @@ public class AboutActivity extends Activity {
|
|||||||
@BindView(R.id.app_translators) AboutCardRow appTranslators;
|
@BindView(R.id.app_translators) AboutCardRow appTranslators;
|
||||||
@BindView(R.id.app_source_code) AboutCardRow appSourceCode;
|
@BindView(R.id.app_source_code) AboutCardRow appSourceCode;
|
||||||
@BindView(R.id.support_thread) AboutCardRow supportThread;
|
@BindView(R.id.support_thread) AboutCardRow supportThread;
|
||||||
@BindView(R.id.donation) AboutCardRow donation;
|
@BindView(R.id.follow_twitter) AboutCardRow twitter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getDarkTheme() {
|
public int getDarkTheme() {
|
||||||
@@ -38,7 +36,7 @@ public class AboutActivity extends Activity {
|
|||||||
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());
|
||||||
@@ -52,7 +50,6 @@ public class AboutActivity extends Activity {
|
|||||||
appVersionInfo.setSummary(String.format(Locale.US, "%s (%d) (%s)",
|
appVersionInfo.setSummary(String.format(Locale.US, "%s (%d) (%s)",
|
||||||
BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, getPackageName()));
|
BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, getPackageName()));
|
||||||
|
|
||||||
appChangelog.removeSummary();
|
|
||||||
appChangelog.setOnClickListener(v -> {
|
appChangelog.setOnClickListener(v -> {
|
||||||
new MarkDownWindow(this, getString(R.string.app_changelog),
|
new MarkDownWindow(this, getString(R.string.app_changelog),
|
||||||
getResources().openRawResource(R.raw.changelog)).exec();
|
getResources().openRawResource(R.raw.changelog)).exec();
|
||||||
@@ -65,14 +62,9 @@ public class AboutActivity extends Activity {
|
|||||||
appTranslators.setSummary(translators);
|
appTranslators.setSummary(translators);
|
||||||
}
|
}
|
||||||
|
|
||||||
appSourceCode.removeSummary();
|
appSourceCode.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.SOURCE_CODE_URL)));
|
||||||
appSourceCode.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Const.Url.SOURCE_CODE_URL))));
|
supportThread.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.XDA_THREAD)));
|
||||||
|
twitter.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.TWITTER_URL)));
|
||||||
supportThread.removeSummary();
|
|
||||||
supportThread.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Const.Url.XDA_THREAD))));
|
|
||||||
|
|
||||||
donation.removeSummary();
|
|
||||||
donation.setOnClickListener(view -> startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Const.Url.DONATION_URL))));
|
|
||||||
|
|
||||||
setFloating();
|
setFloating();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
import android.os.Environment;
|
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.BuildConfig;
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -34,47 +30,39 @@ public class Const {
|
|||||||
public static final String BUSYBOX_PATH = "/sbin/.core/busybox";
|
public static final String BUSYBOX_PATH = "/sbin/.core/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 File EXTERNAL_PATH = new File(Environment.getExternalStorageDirectory(), "MagiskManager");
|
|
||||||
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 SNET_VER = 8;
|
|
||||||
|
|
||||||
public static int MIN_MODULE_VER() {
|
public static int MIN_MODULE_VER() {
|
||||||
return MagiskManager.get().magiskVersionCode >= MAGISK_VER.REMOVE_LEGACY_LINK ? 1500 : 1400;
|
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(),
|
||||||
MagiskManager.get().getPackageName(),
|
|
||||||
"com.google.android.gms"
|
"com.google.android.gms"
|
||||||
);
|
);
|
||||||
|
|
||||||
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 DTBO_SUPPORT = 1446;
|
|
||||||
public static final int HIDDEN_PATH = 1460;
|
|
||||||
public static final int REMOVE_LEGACY_LINK = 1630;
|
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 class ID {
|
public static class ID {
|
||||||
public static final int UPDATE_SERVICE_ID = 1;
|
public static final int UPDATE_SERVICE_ID = 1;
|
||||||
public static final int FETCH_ZIP = 2;
|
public static final int FETCH_ZIP = 2;
|
||||||
public static final int SELECT_BOOT = 3;
|
public static final int SELECT_BOOT = 3;
|
||||||
|
public static final int ONBOOT_SERVICE_ID = 6;
|
||||||
|
|
||||||
// notifications
|
// notifications
|
||||||
public static final int MAGISK_UPDATE_NOTIFICATION_ID = 4;
|
public static final int MAGISK_UPDATE_NOTIFICATION_ID = 4;
|
||||||
public static final int APK_UPDATE_NOTIFICATION_ID = 5;
|
public static final int APK_UPDATE_NOTIFICATION_ID = 5;
|
||||||
public static final int ONBOOT_NOTIFICATION_ID = 6;
|
|
||||||
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 String NOTIFICATION_CHANNEL = "magisk_notification";
|
||||||
}
|
}
|
||||||
@@ -82,11 +70,12 @@ public class Const {
|
|||||||
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 SNET_URL = "https://github.com/topjohnwu/magisk_files/raw/727aa3a8642bf5f0982e5ea89b3f818bd783d5a2/snet.apk";
|
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&page=%d";
|
|
||||||
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 DONATION_URL = "https://www.paypal.me/topjohnwu";
|
public static final String PAYPAL_URL = "https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=CC7FZ7526MNGG";
|
||||||
|
public static final String PATREON_URL = "https://www.patreon.com/topjohnwu";
|
||||||
|
public static final String TWITTER_URL = "https://twitter.com/topjohnwu";
|
||||||
public static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382";
|
public static final String 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";
|
||||||
}
|
}
|
||||||
@@ -108,7 +97,6 @@ public class Const {
|
|||||||
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_FILENAME = "filename";
|
||||||
public static final String INTENT_SET_LINK = "link";
|
public static final String INTENT_SET_LINK = "link";
|
||||||
public static final String INTENT_PERM = "perm_dialog";
|
|
||||||
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";
|
||||||
|
|
||||||
@@ -147,16 +135,13 @@ 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;
|
||||||
public static final String FLASH_ZIP = "flash";
|
public static final String FLASH_ZIP = "flash";
|
||||||
public static final String PATCH_BOOT = "patch";
|
public static final String PATCH_BOOT = "patch";
|
||||||
public static final String FLASH_MAGISK = "magisk";
|
public static final String FLASH_MAGISK = "magisk";
|
||||||
public static final String FLASH_SECOND_SLOT = "slot";
|
public static final String FLASH_INACTIVE_SLOT = "slot";
|
||||||
public static final String UNINSTALL = "uninstall";
|
public static final String UNINSTALL = "uninstall";
|
||||||
public static final int[] timeoutList = {0, -1, 10, 20, 30, 60};
|
public static final int[] timeoutList = {0, -1, 10, 20, 30, 60};
|
||||||
public static final int ORDER_NAME = 0;
|
public static final int ORDER_NAME = 0;
|
||||||
217
app/src/full/java/com/topjohnwu/magisk/Data.java
Normal file
217
app/src/full/java/com/topjohnwu/magisk/Data.java
Normal file
@@ -0,0 +1,217 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
|
import android.util.Xml;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.components.AboutCardRow;
|
||||||
|
import com.topjohnwu.magisk.receivers.BootReceiver;
|
||||||
|
import com.topjohnwu.magisk.receivers.ManagerUpdate;
|
||||||
|
import com.topjohnwu.magisk.receivers.PackageReceiver;
|
||||||
|
import com.topjohnwu.magisk.receivers.RebootReceiver;
|
||||||
|
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
|
||||||
|
import com.topjohnwu.magisk.services.OnBootService;
|
||||||
|
import com.topjohnwu.magisk.services.UpdateCheckService;
|
||||||
|
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
import com.topjohnwu.superuser.io.SuFileInputStream;
|
||||||
|
|
||||||
|
import org.xmlpull.v1.XmlPullParser;
|
||||||
|
import org.xmlpull.v1.XmlPullParserException;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class Data {
|
||||||
|
// Global app instance
|
||||||
|
public static WeakReference<MagiskManager> weakApp;
|
||||||
|
public static Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
|
public static Map<Class, Class> classMap = new HashMap<>();
|
||||||
|
|
||||||
|
// Current status
|
||||||
|
public static String magiskVersionString;
|
||||||
|
public static int magiskVersionCode = -1;
|
||||||
|
public static boolean magiskHide;
|
||||||
|
|
||||||
|
// Update Info
|
||||||
|
public static String remoteMagiskVersionString;
|
||||||
|
public static int remoteMagiskVersionCode = -1;
|
||||||
|
public static String magiskLink;
|
||||||
|
public static String magiskNoteLink;
|
||||||
|
public static String magiskMD5;
|
||||||
|
public static String remoteManagerVersionString;
|
||||||
|
public static int remoteManagerVersionCode = -1;
|
||||||
|
public static String managerLink;
|
||||||
|
public static String managerNoteLink;
|
||||||
|
public static String uninstallerLink;
|
||||||
|
public static int snetVersionCode;
|
||||||
|
public static String snetLink;
|
||||||
|
|
||||||
|
// Install flags
|
||||||
|
public static boolean keepVerity = false;
|
||||||
|
public static boolean keepEnc = false;
|
||||||
|
|
||||||
|
// Configs
|
||||||
|
public static boolean isDarkTheme;
|
||||||
|
public static int suRequestTimeout;
|
||||||
|
public static int suLogTimeout = 14;
|
||||||
|
public static int suAccessState;
|
||||||
|
public static boolean suFingerprint;
|
||||||
|
public static int multiuserMode;
|
||||||
|
public static int suResponseType;
|
||||||
|
public static int suNotificationType;
|
||||||
|
public static int suNamespaceMode;
|
||||||
|
public static int updateChannel;
|
||||||
|
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(BootReceiver.class, a.h.class);
|
||||||
|
classMap.put(PackageReceiver.class, a.i.class);
|
||||||
|
classMap.put(ManagerUpdate.class, a.j.class);
|
||||||
|
classMap.put(RebootReceiver.class, a.k.class);
|
||||||
|
classMap.put(ShortcutReceiver.class, a.l.class);
|
||||||
|
classMap.put(OnBootService.class, a.m.class);
|
||||||
|
classMap.put(UpdateCheckService.class, a.n.class);
|
||||||
|
classMap.put(AboutCardRow.class, a.o.class);
|
||||||
|
classMap.put(SuRequestActivity.class, a.p.class);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadMagiskInfo() {
|
||||||
|
try {
|
||||||
|
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
|
||||||
|
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
|
||||||
|
String s = ShellUtils.fastCmd(("resetprop -p ") + Const.MAGISKHIDE_PROP);
|
||||||
|
magiskHide = s.isEmpty() || Integer.parseInt(s) != 0;
|
||||||
|
} catch (NumberFormatException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static MagiskManager MM() {
|
||||||
|
return weakApp.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void exportPrefs() {
|
||||||
|
// Flush prefs to disk
|
||||||
|
MagiskManager mm = MM();
|
||||||
|
mm.prefs.edit().commit();
|
||||||
|
File xml = new File(mm.getFilesDir().getParent() + "/shared_prefs",
|
||||||
|
mm.getPackageName() + "_preferences.xml");
|
||||||
|
Shell.su(Utils.fmt("for usr in /data/user/*; do cat %s > ${usr}/%s; done", xml, Const.MANAGER_CONFIGS)).exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void importPrefs() {
|
||||||
|
MagiskManager mm = MM();
|
||||||
|
SuFile config = new SuFile(Utils.fmt("/data/user/%d/%s", Const.USER_ID, Const.MANAGER_CONFIGS));
|
||||||
|
if (config.exists()) {
|
||||||
|
SharedPreferences.Editor editor = mm.prefs.edit();
|
||||||
|
try {
|
||||||
|
SuFileInputStream is = new SuFileInputStream(config);
|
||||||
|
XmlPullParser parser = Xml.newPullParser();
|
||||||
|
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
|
||||||
|
parser.setInput(is, "UTF-8");
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "map");
|
||||||
|
while (parser.next() != XmlPullParser.END_TAG) {
|
||||||
|
if (parser.getEventType() != XmlPullParser.START_TAG)
|
||||||
|
continue;
|
||||||
|
String key = parser.getAttributeValue(null, "name");
|
||||||
|
String value = parser.getAttributeValue(null, "value");
|
||||||
|
switch (parser.getName()) {
|
||||||
|
case "string":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "string");
|
||||||
|
editor.putString(key, parser.nextText());
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "string");
|
||||||
|
break;
|
||||||
|
case "boolean":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "boolean");
|
||||||
|
editor.putBoolean(key, Boolean.parseBoolean(value));
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "boolean");
|
||||||
|
break;
|
||||||
|
case "int":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "int");
|
||||||
|
editor.putInt(key, Integer.parseInt(value));
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "int");
|
||||||
|
break;
|
||||||
|
case "long":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "long");
|
||||||
|
editor.putLong(key, Long.parseLong(value));
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "long");
|
||||||
|
break;
|
||||||
|
case "float":
|
||||||
|
parser.require(XmlPullParser.START_TAG, null, "int");
|
||||||
|
editor.putFloat(key, Float.parseFloat(value));
|
||||||
|
parser.nextTag();
|
||||||
|
parser.require(XmlPullParser.END_TAG, null, "int");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
parser.next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (IOException | XmlPullParserException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
editor.remove(Const.Key.ETAG_KEY);
|
||||||
|
editor.apply();
|
||||||
|
loadConfig();
|
||||||
|
config.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadConfig() {
|
||||||
|
MagiskManager mm = MM();
|
||||||
|
// su
|
||||||
|
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);
|
||||||
|
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
|
||||||
|
isDarkTheme = mm.prefs.getBoolean(Const.Key.DARK_THEME, false);
|
||||||
|
updateChannel = Utils.getPrefsInt(mm.prefs, Const.Key.UPDATE_CHANNEL, Const.Value.STABLE_CHANNEL);
|
||||||
|
repoOrder = mm.prefs.getInt(Const.Key.REPO_ORDER, Const.Value.ORDER_DATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeConfig() {
|
||||||
|
MM().prefs.edit()
|
||||||
|
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
|
||||||
|
.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.SU_FINGERPRINT, suFingerprint)
|
||||||
|
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(suRequestTimeout))
|
||||||
|
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType))
|
||||||
|
.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))
|
||||||
|
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER)
|
||||||
|
.putInt(Const.Key.REPO_ORDER, repoOrder)
|
||||||
|
.apply();
|
||||||
|
}
|
||||||
|
}
|
||||||
44
app/src/full/java/com/topjohnwu/magisk/DonationActivity.java
Normal file
44
app/src/full/java/com/topjohnwu/magisk/DonationActivity.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.components.AboutCardRow;
|
||||||
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import butterknife.BindView;
|
||||||
|
|
||||||
|
public class DonationActivity extends BaseActivity {
|
||||||
|
|
||||||
|
@BindView(R.id.toolbar) Toolbar toolbar;
|
||||||
|
@BindView(R.id.paypal) AboutCardRow paypal;
|
||||||
|
@BindView(R.id.patreon) AboutCardRow patreon;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getDarkTheme() {
|
||||||
|
return R.style.AppTheme_StatusBar_Dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_donation);
|
||||||
|
new DonationActivity_ViewBinding(this);
|
||||||
|
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
toolbar.setNavigationOnClickListener(view -> finish());
|
||||||
|
|
||||||
|
ActionBar ab = getSupportActionBar();
|
||||||
|
if (ab != null) {
|
||||||
|
ab.setTitle(R.string.donation);
|
||||||
|
ab.setDisplayHomeAsUpEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
paypal.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.PAYPAL_URL)));
|
||||||
|
patreon.setOnClickListener(v -> Utils.openLink(this, Uri.parse(Const.Url.PATREON_URL)));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,9 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
|
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.os.Handler;
|
|
||||||
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;
|
||||||
@@ -16,9 +14,10 @@ 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.Activity;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
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.superuser.CallbackList;
|
import com.topjohnwu.superuser.CallbackList;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
@@ -30,11 +29,12 @@ 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 Activity {
|
public class FlashActivity extends BaseActivity {
|
||||||
|
|
||||||
@BindView(R.id.toolbar) Toolbar toolbar;
|
@BindView(R.id.toolbar) Toolbar toolbar;
|
||||||
@BindView(R.id.txtLog) TextView flashLogs;
|
@BindView(R.id.txtLog) TextView flashLogs;
|
||||||
@@ -44,37 +44,33 @@ public class FlashActivity extends Activity {
|
|||||||
|
|
||||||
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.Async.su("/system/bin/reboot");
|
Shell.su("/system/bin/reboot").submit();
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnClick(R.id.save_logs)
|
@OnClick(R.id.save_logs)
|
||||||
void saveLogs() {
|
void saveLogs() {
|
||||||
Calendar now = Calendar.getInstance();
|
runWithPermission(new String[] {Manifest.permission.WRITE_EXTERNAL_STORAGE}, () -> {
|
||||||
String filename = String.format(Locale.US,
|
Calendar now = Calendar.getInstance();
|
||||||
"install_log_%04d%02d%02d_%02d%02d%02d.log",
|
String filename = String.format(Locale.US,
|
||||||
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
|
"magisk_install_log_%04d%02d%02d_%02d%02d%02d.log",
|
||||||
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
|
||||||
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
|
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
|
||||||
|
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
|
||||||
|
|
||||||
File logFile = new File(Const.EXTERNAL_PATH + "/logs", filename);
|
File logFile = new File(Download.EXTERNAL_PATH, filename);
|
||||||
logFile.getParentFile().mkdirs();
|
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);
|
writer.write('\n');
|
||||||
writer.write('\n');
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
Utils.toast(logFile.getPath(), Toast.LENGTH_LONG);
|
||||||
e.printStackTrace();
|
});
|
||||||
return;
|
|
||||||
}
|
|
||||||
MagiskManager.toast(logFile.getPath(), Toast.LENGTH_LONG);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -86,7 +82,8 @@ public class FlashActivity extends Activity {
|
|||||||
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) {
|
||||||
@@ -99,11 +96,23 @@ public class FlashActivity extends Activity {
|
|||||||
|
|
||||||
logs = new ArrayList<>();
|
logs = new ArrayList<>();
|
||||||
CallbackList<String> console = new CallbackList<String>(new ArrayList<>()) {
|
CallbackList<String> console = new CallbackList<String>(new ArrayList<>()) {
|
||||||
|
|
||||||
|
private void updateUI() {
|
||||||
|
flashLogs.setText(TextUtils.join("\n", this));
|
||||||
|
sv.postDelayed(() -> sv.fullScroll(ScrollView.FOCUS_DOWN), 10);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAddElement(String s) {
|
public void onAddElement(String s) {
|
||||||
logs.add(s);
|
logs.add(s);
|
||||||
flashLogs.setText(TextUtils.join("\n", this));
|
updateUI();
|
||||||
sv.postDelayed(() -> sv.fullScroll(ScrollView.FOCUS_DOWN), 10);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String set(int i, String s) {
|
||||||
|
String ret = super.set(i, s);
|
||||||
|
Data.mainHandler.post(this::updateUI);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -119,18 +128,24 @@ public class FlashActivity extends Activity {
|
|||||||
new UninstallMagisk(this, uri, console, logs).exec();
|
new UninstallMagisk(this, uri, console, logs).exec();
|
||||||
break;
|
break;
|
||||||
case Const.Value.FLASH_MAGISK:
|
case Const.Value.FLASH_MAGISK:
|
||||||
new InstallMagisk(this, console, logs, uri, InstallMagisk.DIRECT_MODE).exec();
|
new InstallMagisk(this, console, logs, InstallMagisk.DIRECT_MODE).exec();
|
||||||
break;
|
break;
|
||||||
case Const.Value.FLASH_SECOND_SLOT:
|
case Const.Value.FLASH_INACTIVE_SLOT:
|
||||||
new InstallMagisk(this, console, logs, uri, InstallMagisk.SECOND_SLOT_MODE).exec();
|
new InstallMagisk(this, console, logs, InstallMagisk.SECOND_SLOT_MODE).exec();
|
||||||
break;
|
break;
|
||||||
case Const.Value.PATCH_BOOT:
|
case Const.Value.PATCH_BOOT:
|
||||||
new InstallMagisk(this, console, logs, uri,
|
new InstallMagisk(this, console, logs,
|
||||||
intent.getParcelableExtra(Const.Key.FLASH_SET_BOOT)).exec();
|
intent.getParcelableExtra(Const.Key.FLASH_SET_BOOT)).exec();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
@@ -138,14 +153,14 @@ public class FlashActivity extends Activity {
|
|||||||
|
|
||||||
private static class UninstallMagisk extends FlashZip {
|
private static class UninstallMagisk extends FlashZip {
|
||||||
|
|
||||||
private UninstallMagisk(Activity context, Uri uri, List<String> console, List<String> logs) {
|
private UninstallMagisk(BaseActivity context, Uri uri, List<String> console, List<String> logs) {
|
||||||
super(context, uri, console, logs);
|
super(context, uri, console, logs);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Integer result) {
|
protected void onPostExecute(Integer result) {
|
||||||
if (result == 1) {
|
if (result == 1) {
|
||||||
new Handler().postDelayed(() ->
|
Data.mainHandler.postDelayed(() ->
|
||||||
RootUtils.uninstallPkg(getActivity().getPackageName()), 3000);
|
RootUtils.uninstallPkg(getActivity().getPackageName()), 3000);
|
||||||
} else {
|
} else {
|
||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
|
|||||||
@@ -1,127 +1,49 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
import android.app.job.JobInfo;
|
|
||||||
import android.app.job.JobScheduler;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Xml;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.components.Application;
|
|
||||||
import com.topjohnwu.magisk.container.Module;
|
|
||||||
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
|
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
|
||||||
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
||||||
import com.topjohnwu.magisk.services.UpdateCheckService;
|
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
import com.topjohnwu.magisk.utils.RootUtils;
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
import com.topjohnwu.magisk.utils.ShellInitializer;
|
import com.topjohnwu.superuser.ContainerApp;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
|
||||||
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.io.SuFile;
|
|
||||||
import com.topjohnwu.superuser.io.SuFileInputStream;
|
|
||||||
|
|
||||||
import org.xmlpull.v1.XmlPullParser;
|
|
||||||
import org.xmlpull.v1.XmlPullParserException;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
public class MagiskManager extends Application implements Shell.Container {
|
public class MagiskManager extends ContainerApp {
|
||||||
|
|
||||||
// Topics
|
|
||||||
public final Topic magiskHideDone = new Topic();
|
|
||||||
public final Topic reloadActivity = new Topic();
|
|
||||||
public final Topic moduleLoadDone = new Topic();
|
|
||||||
public final Topic repoLoadDone = new Topic();
|
|
||||||
public final Topic updateCheckDone = new Topic();
|
|
||||||
public final Topic safetyNetDone = new Topic();
|
|
||||||
public final Topic localeDone = new Topic();
|
|
||||||
|
|
||||||
// Info
|
// Info
|
||||||
public boolean hasInit = false;
|
public boolean hasInit = false;
|
||||||
public String magiskVersionString;
|
|
||||||
public int magiskVersionCode = -1;
|
|
||||||
public String remoteMagiskVersionString;
|
|
||||||
public int remoteMagiskVersionCode = -1;
|
|
||||||
public String remoteManagerVersionString;
|
|
||||||
public int remoteManagerVersionCode = -1;
|
|
||||||
|
|
||||||
public String magiskLink;
|
|
||||||
public String magiskNoteLink;
|
|
||||||
public String managerLink;
|
|
||||||
public String managerNoteLink;
|
|
||||||
public String uninstallerLink;
|
|
||||||
|
|
||||||
public boolean keepVerity = false;
|
|
||||||
public boolean keepEnc = false;
|
|
||||||
|
|
||||||
// Data
|
|
||||||
public Map<String, Module> moduleMap;
|
|
||||||
public List<Locale> locales;
|
|
||||||
|
|
||||||
public boolean magiskHide;
|
|
||||||
public boolean isDarkTheme;
|
|
||||||
public int suRequestTimeout;
|
|
||||||
public int suLogTimeout = 14;
|
|
||||||
public int suAccessState;
|
|
||||||
public int multiuserMode;
|
|
||||||
public int suResponseType;
|
|
||||||
public int suNotificationType;
|
|
||||||
public int suNamespaceMode;
|
|
||||||
public String localeConfig;
|
|
||||||
public int updateChannel;
|
|
||||||
public String bootFormat;
|
|
||||||
public int repoOrder;
|
|
||||||
|
|
||||||
// Global resources
|
// Global resources
|
||||||
public SharedPreferences prefs;
|
public SharedPreferences prefs;
|
||||||
public MagiskDatabaseHelper mDB;
|
public MagiskDatabaseHelper mDB;
|
||||||
public RepoDatabaseHelper repoDB;
|
public RepoDatabaseHelper repoDB;
|
||||||
|
|
||||||
private volatile Shell mShell;
|
|
||||||
|
|
||||||
public MagiskManager() {
|
public MagiskManager() {
|
||||||
weakSelf = new WeakReference<>(this);
|
Data.weakApp = new WeakReference<>(this);
|
||||||
Shell.setContainer(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Shell getShell() {
|
|
||||||
return mShell;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setShell(@Nullable Shell shell) {
|
|
||||||
mShell = shell;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
||||||
Shell.setFlags(Shell.FLAG_MOUNT_MASTER);
|
Shell.Config.setFlags(Shell.FLAG_MOUNT_MASTER);
|
||||||
Shell.verboseLogging(BuildConfig.DEBUG);
|
Shell.Config.verboseLogging(BuildConfig.DEBUG);
|
||||||
Shell.setInitializer(ShellInitializer.class);
|
Shell.Config.setInitializer(RootUtils.class);
|
||||||
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||||
mDB = MagiskDatabaseHelper.getInstance(this);
|
mDB = MagiskDatabaseHelper.getInstance();
|
||||||
|
|
||||||
String pkg = mDB.getStrings(Const.Key.SU_MANAGER, null);
|
String pkg = mDB.getStrings(Const.Key.SU_MANAGER, null);
|
||||||
if (pkg != null && getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
if (pkg != null && getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
||||||
mDB.setStrings(Const.Key.SU_MANAGER, null);
|
mDB.setStrings(Const.Key.SU_MANAGER, null);
|
||||||
RootUtils.uninstallPkg(pkg);
|
Shell.su("pm uninstall " + pkg).exec();
|
||||||
}
|
}
|
||||||
if (TextUtils.equals(pkg, getPackageName())) {
|
if (TextUtils.equals(pkg, getPackageName())) {
|
||||||
try {
|
try {
|
||||||
@@ -131,162 +53,13 @@ public class MagiskManager extends Application implements Shell.Container {
|
|||||||
} catch (PackageManager.NameNotFoundException ignored) {}
|
} catch (PackageManager.NameNotFoundException ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
setLocale();
|
LocaleManager.setLocale(this);
|
||||||
loadConfig();
|
Data.loadConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MagiskManager get() {
|
@Override
|
||||||
return (MagiskManager) weakSelf.get();
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
}
|
super.onConfigurationChanged(newConfig);
|
||||||
|
LocaleManager.setLocale(this);
|
||||||
public void setLocale() {
|
|
||||||
localeConfig = prefs.getString(Const.Key.LOCALE, "");
|
|
||||||
if (localeConfig.isEmpty()) {
|
|
||||||
locale = defaultLocale;
|
|
||||||
} else {
|
|
||||||
locale = Locale.forLanguageTag(localeConfig);
|
|
||||||
}
|
|
||||||
Resources res = getBaseContext().getResources();
|
|
||||||
Configuration config = new Configuration(res.getConfiguration());
|
|
||||||
config.setLocale(locale);
|
|
||||||
res.updateConfiguration(config, res.getDisplayMetrics());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadConfig() {
|
|
||||||
// su
|
|
||||||
suRequestTimeout = Utils.getPrefsInt(prefs, Const.Key.SU_REQUEST_TIMEOUT, Const.Value.timeoutList[2]);
|
|
||||||
suResponseType = Utils.getPrefsInt(prefs, Const.Key.SU_AUTO_RESPONSE, Const.Value.SU_PROMPT);
|
|
||||||
suNotificationType = Utils.getPrefsInt(prefs, Const.Key.SU_NOTIFICATION, Const.Value.NOTIFICATION_TOAST);
|
|
||||||
suAccessState = mDB.getSettings(Const.Key.ROOT_ACCESS, Const.Value.ROOT_ACCESS_APPS_AND_ADB);
|
|
||||||
multiuserMode = mDB.getSettings(Const.Key.SU_MULTIUSER_MODE, Const.Value.MULTIUSER_MODE_OWNER_ONLY);
|
|
||||||
suNamespaceMode = mDB.getSettings(Const.Key.SU_MNT_NS, Const.Value.NAMESPACE_MODE_REQUESTER);
|
|
||||||
|
|
||||||
// config
|
|
||||||
isDarkTheme = prefs.getBoolean(Const.Key.DARK_THEME, false);
|
|
||||||
updateChannel = Utils.getPrefsInt(prefs, Const.Key.UPDATE_CHANNEL, Const.Value.STABLE_CHANNEL);
|
|
||||||
bootFormat = prefs.getString(Const.Key.BOOT_FORMAT, ".img");
|
|
||||||
repoOrder = prefs.getInt(Const.Key.REPO_ORDER, Const.Value.ORDER_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeConfig() {
|
|
||||||
prefs.edit()
|
|
||||||
.putBoolean(Const.Key.DARK_THEME, isDarkTheme)
|
|
||||||
.putBoolean(Const.Key.MAGISKHIDE, magiskHide)
|
|
||||||
.putBoolean(Const.Key.HOSTS, Const.MAGISK_HOST_FILE.exists())
|
|
||||||
.putBoolean(Const.Key.COREONLY, Const.MAGISK_DISABLE_FILE.exists())
|
|
||||||
.putString(Const.Key.SU_REQUEST_TIMEOUT, String.valueOf(suRequestTimeout))
|
|
||||||
.putString(Const.Key.SU_AUTO_RESPONSE, String.valueOf(suResponseType))
|
|
||||||
.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.LOCALE, localeConfig)
|
|
||||||
.putString(Const.Key.BOOT_FORMAT, bootFormat)
|
|
||||||
.putInt(Const.Key.UPDATE_SERVICE_VER, Const.UPDATE_SERVICE_VER)
|
|
||||||
.putInt(Const.Key.REPO_ORDER, repoOrder)
|
|
||||||
.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadMagiskInfo() {
|
|
||||||
try {
|
|
||||||
magiskVersionString = ShellUtils.fastCmd("magisk -v").split(":")[0];
|
|
||||||
magiskVersionCode = Integer.parseInt(ShellUtils.fastCmd("magisk -V"));
|
|
||||||
String s = ShellUtils.fastCmd((magiskVersionCode >= Const.MAGISK_VER.RESETPROP_PERSIST ?
|
|
||||||
"resetprop -p " : "getprop ") + Const.MAGISKHIDE_PROP);
|
|
||||||
magiskHide = s == null || Integer.parseInt(s) != 0;
|
|
||||||
} catch (Exception ignored) {}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void getDefaultInstallFlags() {
|
|
||||||
keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
|
|
||||||
keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT"));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setupUpdateCheck() {
|
|
||||||
JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
|
|
||||||
|
|
||||||
if (prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) {
|
|
||||||
if (scheduler.getAllPendingJobs().isEmpty() ||
|
|
||||||
Const.UPDATE_SERVICE_VER > prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
|
|
||||||
ComponentName service = new ComponentName(this, UpdateCheckService.class);
|
|
||||||
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
|
|
||||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
|
||||||
.setPersisted(true)
|
|
||||||
.setPeriodic(8 * 60 * 60 * 1000)
|
|
||||||
.build();
|
|
||||||
scheduler.schedule(info);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
scheduler.cancel(Const.UPDATE_SERVICE_VER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dumpPrefs() {
|
|
||||||
// Flush prefs to disk
|
|
||||||
prefs.edit().commit();
|
|
||||||
File xml = new File(getFilesDir().getParent() + "/shared_prefs",
|
|
||||||
getPackageName() + "_preferences.xml");
|
|
||||||
Shell.Sync.su(Utils.fmt("for usr in /data/user/*; do cat %s > ${usr}/%s; done", xml, Const.MANAGER_CONFIGS));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadPrefs() {
|
|
||||||
SuFile config = new SuFile(Utils.fmt("/data/user/%d/%s", Const.USER_ID, Const.MANAGER_CONFIGS));
|
|
||||||
if (config.exists()) {
|
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
|
||||||
try {
|
|
||||||
SuFileInputStream is = new SuFileInputStream(config);
|
|
||||||
XmlPullParser parser = Xml.newPullParser();
|
|
||||||
parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
|
|
||||||
parser.setInput(is, "UTF-8");
|
|
||||||
parser.nextTag();
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "map");
|
|
||||||
while (parser.next() != XmlPullParser.END_TAG) {
|
|
||||||
if (parser.getEventType() != XmlPullParser.START_TAG)
|
|
||||||
continue;
|
|
||||||
String key = parser.getAttributeValue(null, "name");
|
|
||||||
String value = parser.getAttributeValue(null, "value");
|
|
||||||
switch (parser.getName()) {
|
|
||||||
case "string":
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "string");
|
|
||||||
editor.putString(key, parser.nextText());
|
|
||||||
parser.require(XmlPullParser.END_TAG, null, "string");
|
|
||||||
break;
|
|
||||||
case "boolean":
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "boolean");
|
|
||||||
editor.putBoolean(key, Boolean.parseBoolean(value));
|
|
||||||
parser.nextTag();
|
|
||||||
parser.require(XmlPullParser.END_TAG, null, "boolean");
|
|
||||||
break;
|
|
||||||
case "int":
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "int");
|
|
||||||
editor.putInt(key, Integer.parseInt(value));
|
|
||||||
parser.nextTag();
|
|
||||||
parser.require(XmlPullParser.END_TAG, null, "int");
|
|
||||||
break;
|
|
||||||
case "long":
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "long");
|
|
||||||
editor.putLong(key, Long.parseLong(value));
|
|
||||||
parser.nextTag();
|
|
||||||
parser.require(XmlPullParser.END_TAG, null, "long");
|
|
||||||
break;
|
|
||||||
case "float":
|
|
||||||
parser.require(XmlPullParser.START_TAG, null, "int");
|
|
||||||
editor.putFloat(key, Float.parseFloat(value));
|
|
||||||
parser.nextTag();
|
|
||||||
parser.require(XmlPullParser.END_TAG, null, "int");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
parser.next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException | XmlPullParserException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
editor.remove(Const.Key.ETAG_KEY);
|
|
||||||
editor.apply();
|
|
||||||
loadConfig();
|
|
||||||
config.delete();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,37 +3,41 @@ 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.ActivityCompat;
|
|
||||||
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.topjohnwu.magisk.components.Activity;
|
import com.google.android.material.navigation.NavigationView;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
|
import com.topjohnwu.magisk.fragments.LogFragment;
|
||||||
|
import com.topjohnwu.magisk.fragments.MagiskFragment;
|
||||||
|
import com.topjohnwu.magisk.fragments.MagiskHideFragment;
|
||||||
|
import com.topjohnwu.magisk.fragments.ModulesFragment;
|
||||||
|
import com.topjohnwu.magisk.fragments.ReposFragment;
|
||||||
|
import com.topjohnwu.magisk.fragments.SettingsFragment;
|
||||||
|
import com.topjohnwu.magisk.fragments.SuperuserFragment;
|
||||||
|
import com.topjohnwu.magisk.utils.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 Activity
|
public class MainActivity extends BaseActivity
|
||||||
implements NavigationView.OnNavigationItemSelectedListener, Topic.Subscriber {
|
implements NavigationView.OnNavigationItemSelectedListener, Topic.Subscriber {
|
||||||
|
|
||||||
private final Handler mDrawerHandler = new Handler();
|
private final Handler mDrawerHandler = new Handler();
|
||||||
private int mDrawerItem;
|
private int mDrawerItem;
|
||||||
private boolean fromShortcut = true;
|
private static boolean fromShortcut = false;
|
||||||
|
|
||||||
@BindView(R.id.toolbar) Toolbar toolbar;
|
@BindView(R.id.toolbar) public Toolbar toolbar;
|
||||||
@BindView(R.id.drawer_layout) DrawerLayout drawer;
|
@BindView(R.id.drawer_layout) DrawerLayout drawer;
|
||||||
@BindView(R.id.nav_view) public NavigationView navigationView;
|
@BindView(R.id.nav_view) NavigationView navigationView;
|
||||||
|
|
||||||
private float toolbarElevation;
|
private float toolbarElevation;
|
||||||
|
|
||||||
@@ -44,27 +48,14 @@ public class MainActivity extends Activity
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(final Bundle savedInstanceState) {
|
protected void onCreate(final Bundle savedInstanceState) {
|
||||||
|
|
||||||
MagiskManager mm = getMagiskManager();
|
|
||||||
|
|
||||||
if (!mm.hasInit) {
|
if (!mm.hasInit) {
|
||||||
Intent intent = new Intent(this, SplashActivity.class);
|
startActivity(new Intent(this, Data.classMap.get(SplashActivity.class)));
|
||||||
String section = getIntent().getStringExtra(Const.Key.OPEN_SECTION);
|
|
||||||
if (section != null) {
|
|
||||||
intent.putExtra(Const.Key.OPEN_SECTION, section);
|
|
||||||
}
|
|
||||||
startActivity(intent);
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
String perm = getIntent().getStringExtra(Const.Key.INTENT_PERM);
|
|
||||||
if (perm != null) {
|
|
||||||
ActivityCompat.requestPermissions(this, new String[] { perm }, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
@@ -86,8 +77,11 @@ public class MainActivity extends Activity
|
|||||||
drawer.addDrawerListener(toggle);
|
drawer.addDrawerListener(toggle);
|
||||||
toggle.syncState();
|
toggle.syncState();
|
||||||
|
|
||||||
if (savedInstanceState == null)
|
if (savedInstanceState == null) {
|
||||||
navigate(getIntent().getStringExtra(Const.Key.OPEN_SECTION));
|
String section = getIntent().getStringExtra(Const.Key.OPEN_SECTION);
|
||||||
|
fromShortcut = section != null;
|
||||||
|
navigate(section);
|
||||||
|
}
|
||||||
|
|
||||||
navigationView.setNavigationItemSelectedListener(this);
|
navigationView.setNavigationItemSelectedListener(this);
|
||||||
}
|
}
|
||||||
@@ -118,28 +112,20 @@ public class MainActivity extends Activity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTopicPublished(Topic topic) {
|
public void onPublish(int topic, Object[] result) {
|
||||||
recreate();
|
recreate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Topic[] getSubscription() {
|
|
||||||
return new Topic[] { getMagiskManager().reloadActivity };
|
|
||||||
}
|
|
||||||
|
|
||||||
public void checkHideSection() {
|
public void checkHideSection() {
|
||||||
MagiskManager mm = getMagiskManager();
|
|
||||||
Menu menu = navigationView.getMenu();
|
Menu menu = navigationView.getMenu();
|
||||||
menu.findItem(R.id.magiskhide).setVisible(
|
menu.findItem(R.id.magiskhide).setVisible(Shell.rootAccess() &&
|
||||||
Shell.rootAccess() && mm.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(!mm.prefs.getBoolean(Const.Key.COREONLY, false) &&
|
menu.findItem(R.id.downloads).setVisible(Download.checkNetworkStatus(this)
|
||||||
Shell.rootAccess() && mm.magiskVersionCode >= 0);
|
&& Shell.rootAccess() && Data.magiskVersionCode >= 0);
|
||||||
menu.findItem(R.id.downloads).setVisible(!mm.prefs.getBoolean(Const.Key.COREONLY, false)
|
|
||||||
&& Utils.checkNetworkStatus() && Shell.rootAccess() && mm.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(Shell.rootAccess() &&
|
||||||
!(Const.USER_ID > 0 && mm.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED));
|
!(Const.USER_ID > 0 && Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void navigate(String item) {
|
public void navigate(String item) {
|
||||||
@@ -167,6 +153,9 @@ public class MainActivity extends Activity
|
|||||||
case "about":
|
case "about":
|
||||||
itemId = R.id.app_about;
|
itemId = R.id.app_about;
|
||||||
break;
|
break;
|
||||||
|
case "donation":
|
||||||
|
itemId = R.id.donation;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
navigate(itemId);
|
navigate(itemId);
|
||||||
@@ -197,11 +186,14 @@ public class MainActivity extends Activity
|
|||||||
displayFragment(new LogFragment(), false);
|
displayFragment(new LogFragment(), false);
|
||||||
break;
|
break;
|
||||||
case R.id.settings:
|
case R.id.settings:
|
||||||
startActivity(new Intent(this, SettingsActivity.class));
|
displayFragment(new SettingsFragment(), true);
|
||||||
mDrawerItem = bak;
|
|
||||||
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;
|
||||||
|
break;
|
||||||
|
case R.id.donation:
|
||||||
|
startActivity(new Intent(this, Data.classMap.get(DonationActivity.class)));
|
||||||
mDrawerItem = bak;
|
mDrawerItem = bak;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,10 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v4.app.ActivityCompat;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.components.Activity;
|
import androidx.annotation.NonNull;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
|
|
||||||
public class NoUIActivity extends Activity {
|
|
||||||
@Override
|
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
String[] perms = getIntent().getStringArrayExtra(Const.Key.INTENT_PERM);
|
|
||||||
if (perms != null) {
|
|
||||||
ActivityCompat.requestPermissions(this, perms, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
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) {
|
||||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
|
||||||
|
|||||||
@@ -1,321 +0,0 @@
|
|||||||
package com.topjohnwu.magisk;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.v14.preference.SwitchPreference;
|
|
||||||
import android.support.v7.app.ActionBar;
|
|
||||||
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.support.v7.widget.Toolbar;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.EditText;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
|
||||||
import com.topjohnwu.magisk.asyncs.HideManager;
|
|
||||||
import com.topjohnwu.magisk.components.Activity;
|
|
||||||
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
|
||||||
import com.topjohnwu.magisk.utils.RootUtils;
|
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
import butterknife.BindView;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class SettingsActivity extends Activity implements Topic.Subscriber {
|
|
||||||
|
|
||||||
@BindView(R.id.toolbar) Toolbar toolbar;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getDarkTheme() {
|
|
||||||
return R.style.AppTheme_StatusBar_Dark;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
setContentView(R.layout.activity_settings);
|
|
||||||
ButterKnife.bind(this);
|
|
||||||
|
|
||||||
setSupportActionBar(toolbar);
|
|
||||||
|
|
||||||
toolbar.setNavigationOnClickListener(view -> finish());
|
|
||||||
|
|
||||||
ActionBar ab = getSupportActionBar();
|
|
||||||
if (ab != null) {
|
|
||||||
ab.setTitle(R.string.settings);
|
|
||||||
ab.setDisplayHomeAsUpEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
setFloating();
|
|
||||||
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
getSupportFragmentManager().beginTransaction().add(R.id.container, new SettingsFragment()).commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTopicPublished(Topic topic) {
|
|
||||||
recreate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Topic[] getSubscription() {
|
|
||||||
return new Topic[] { getMagiskManager().reloadActivity };
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class SettingsFragment extends PreferenceFragmentCompat
|
|
||||||
implements SharedPreferences.OnSharedPreferenceChangeListener, Topic.Subscriber {
|
|
||||||
|
|
||||||
private SharedPreferences prefs;
|
|
||||||
private PreferenceScreen prefScreen;
|
|
||||||
|
|
||||||
private ListPreference updateChannel, suAccess, autoRes, suNotification,
|
|
||||||
requestTimeout, multiuserMode, namespaceMode;
|
|
||||||
private MagiskManager mm;
|
|
||||||
private PreferenceCategory generalCatagory;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
|
||||||
setPreferencesFromResource(R.xml.app_settings, rootKey);
|
|
||||||
mm = Utils.getMagiskManager(getActivity());
|
|
||||||
prefs = mm.prefs;
|
|
||||||
prefScreen = getPreferenceScreen();
|
|
||||||
|
|
||||||
generalCatagory = (PreferenceCategory) findPreference("general");
|
|
||||||
PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk");
|
|
||||||
PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser");
|
|
||||||
Preference hideManager = findPreference("hide");
|
|
||||||
Preference restoreManager = findPreference("restore");
|
|
||||||
findPreference("clear").setOnPreferenceClickListener((pref) -> {
|
|
||||||
prefs.edit().remove(Const.Key.ETAG_KEY).apply();
|
|
||||||
mm.repoDB.clearRepo();
|
|
||||||
MagiskManager.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
updateChannel = (ListPreference) findPreference(Const.Key.UPDATE_CHANNEL);
|
|
||||||
suAccess = (ListPreference) findPreference(Const.Key.ROOT_ACCESS);
|
|
||||||
autoRes = (ListPreference) findPreference(Const.Key.SU_AUTO_RESPONSE);
|
|
||||||
requestTimeout = (ListPreference) findPreference(Const.Key.SU_REQUEST_TIMEOUT);
|
|
||||||
suNotification = (ListPreference) findPreference(Const.Key.SU_NOTIFICATION);
|
|
||||||
multiuserMode = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE);
|
|
||||||
namespaceMode = (ListPreference) findPreference(Const.Key.SU_MNT_NS);
|
|
||||||
SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH);
|
|
||||||
SwitchPreference fingerprint = (SwitchPreference) findPreference(Const.Key.SU_FINGERPRINT);
|
|
||||||
|
|
||||||
updateChannel.setOnPreferenceChangeListener((pref, o) -> {
|
|
||||||
mm.updateChannel = Integer.parseInt((String) o);
|
|
||||||
if (mm.updateChannel == Const.Value.CUSTOM_CHANNEL) {
|
|
||||||
View v = LayoutInflater.from(getActivity()).inflate(R.layout.custom_channel_dialog, null);
|
|
||||||
EditText url = v.findViewById(R.id.custom_url);
|
|
||||||
url.setText(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
|
|
||||||
new AlertDialog.Builder(getActivity())
|
|
||||||
.setTitle(R.string.settings_update_custom)
|
|
||||||
.setView(v)
|
|
||||||
.setPositiveButton(R.string.ok, (d, i) ->
|
|
||||||
prefs.edit().putString(Const.Key.CUSTOM_CHANNEL,
|
|
||||||
url.getText().toString()).apply())
|
|
||||||
.setNegativeButton(R.string.close, null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
|
|
||||||
setSummary();
|
|
||||||
|
|
||||||
// Disable dangerous settings in secondary user
|
|
||||||
if (Const.USER_ID > 0) {
|
|
||||||
suCategory.removePreference(multiuserMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable re-authentication option on Android O, it will not work
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
reauth.setEnabled(false);
|
|
||||||
reauth.setSummary(R.string.android_o_not_support);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Disable fingerprint option if not possible
|
|
||||||
if (!FingerprintHelper.canUseFingerprint()) {
|
|
||||||
fingerprint.setEnabled(false);
|
|
||||||
fingerprint.setSummary(R.string.disable_fingerprint);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mm.magiskVersionCode >= Const.MAGISK_VER.MANAGER_HIDE) {
|
|
||||||
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
|
||||||
hideManager.setOnPreferenceClickListener((pref) -> {
|
|
||||||
new HideManager(getActivity()).exec();
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
generalCatagory.removePreference(restoreManager);
|
|
||||||
} else {
|
|
||||||
if (Utils.checkNetworkStatus()) {
|
|
||||||
restoreManager.setOnPreferenceClickListener((pref) -> {
|
|
||||||
Utils.dlAndReceive(
|
|
||||||
getActivity(), new DownloadReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onDownloadDone(Context context, Uri uri) {
|
|
||||||
mm.dumpPrefs();
|
|
||||||
if (ShellUtils.fastCmdResult("pm install " + uri.getPath()))
|
|
||||||
RootUtils.uninstallPkg(context.getPackageName());
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mm.managerLink,
|
|
||||||
Utils.fmt("MagiskManager-v%s.apk", mm.remoteManagerVersionString)
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
generalCatagory.removePreference(restoreManager);
|
|
||||||
}
|
|
||||||
generalCatagory.removePreference(hideManager);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
generalCatagory.removePreference(restoreManager);
|
|
||||||
generalCatagory.removePreference(hideManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Shell.rootAccess() || (Const.USER_ID > 0 &&
|
|
||||||
mm.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
|
|
||||||
prefScreen.removePreference(suCategory);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Shell.rootAccess()) {
|
|
||||||
prefScreen.removePreference(magiskCategory);
|
|
||||||
generalCatagory.removePreference(hideManager);
|
|
||||||
} else if (mm.magiskVersionCode < Const.MAGISK_VER.UNIFIED) {
|
|
||||||
prefScreen.removePreference(magiskCategory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setLocalePreference(ListPreference lp) {
|
|
||||||
CharSequence[] entries = new CharSequence[mm.locales.size() + 1];
|
|
||||||
CharSequence[] entryValues = new CharSequence[mm.locales.size() + 1];
|
|
||||||
entries[0] = Utils.getLocaleString(MagiskManager.defaultLocale, R.string.system_default);
|
|
||||||
entryValues[0] = "";
|
|
||||||
int i = 1;
|
|
||||||
for (Locale locale : mm.locales) {
|
|
||||||
entries[i] = locale.getDisplayName(locale);
|
|
||||||
entryValues[i++] = locale.toLanguageTag();
|
|
||||||
}
|
|
||||||
lp.setEntries(entries);
|
|
||||||
lp.setEntryValues(entryValues);
|
|
||||||
lp.setSummary(MagiskManager.locale.getDisplayName(MagiskManager.locale));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
prefs.registerOnSharedPreferenceChangeListener(this);
|
|
||||||
subscribeTopics();
|
|
||||||
return super.onCreateView(inflater, container, savedInstanceState);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
prefs.unregisterOnSharedPreferenceChangeListener(this);
|
|
||||||
unsubscribeTopics();
|
|
||||||
super.onDestroyView();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case Const.Key.DARK_THEME:
|
|
||||||
mm.isDarkTheme = prefs.getBoolean(key, false);
|
|
||||||
mm.reloadActivity.publish(false);
|
|
||||||
return;
|
|
||||||
case Const.Key.COREONLY:
|
|
||||||
if (prefs.getBoolean(key, false)) {
|
|
||||||
try {
|
|
||||||
Const.MAGISK_DISABLE_FILE.createNewFile();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
} else {
|
|
||||||
Const.MAGISK_DISABLE_FILE.delete();
|
|
||||||
}
|
|
||||||
Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show();
|
|
||||||
break;
|
|
||||||
case Const.Key.MAGISKHIDE:
|
|
||||||
if (prefs.getBoolean(key, false)) {
|
|
||||||
Shell.Async.su("magiskhide --enable");
|
|
||||||
} else {
|
|
||||||
Shell.Async.su("magiskhide --disable");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Const.Key.HOSTS:
|
|
||||||
if (prefs.getBoolean(key, false)) {
|
|
||||||
Shell.Async.su(
|
|
||||||
"cp -af /system/etc/hosts " + Const.MAGISK_HOST_FILE,
|
|
||||||
"mount -o bind " + Const.MAGISK_HOST_FILE + " /system/etc/hosts");
|
|
||||||
} else {
|
|
||||||
Shell.Async.su(
|
|
||||||
"umount -l /system/etc/hosts",
|
|
||||||
"rm -f " + Const.MAGISK_HOST_FILE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Const.Key.ROOT_ACCESS:
|
|
||||||
case Const.Key.SU_MULTIUSER_MODE:
|
|
||||||
case Const.Key.SU_MNT_NS:
|
|
||||||
mm.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
|
|
||||||
break;
|
|
||||||
case Const.Key.LOCALE:
|
|
||||||
mm.setLocale();
|
|
||||||
mm.reloadActivity.publish(false);
|
|
||||||
break;
|
|
||||||
case Const.Key.UPDATE_CHANNEL:
|
|
||||||
new CheckUpdates().exec();
|
|
||||||
break;
|
|
||||||
case Const.Key.CHECK_UPDATES:
|
|
||||||
mm.setupUpdateCheck();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
mm.loadConfig();
|
|
||||||
setSummary();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setSummary() {
|
|
||||||
updateChannel.setSummary(getResources()
|
|
||||||
.getStringArray(R.array.update_channel)[mm.updateChannel]);
|
|
||||||
suAccess.setSummary(getResources()
|
|
||||||
.getStringArray(R.array.su_access)[mm.suAccessState]);
|
|
||||||
autoRes.setSummary(getResources()
|
|
||||||
.getStringArray(R.array.auto_response)[mm.suResponseType]);
|
|
||||||
suNotification.setSummary(getResources()
|
|
||||||
.getStringArray(R.array.su_notification)[mm.suNotificationType]);
|
|
||||||
requestTimeout.setSummary(
|
|
||||||
getString(R.string.request_timeout_summary, prefs.getString(Const.Key.SU_REQUEST_TIMEOUT, "10")));
|
|
||||||
multiuserMode.setSummary(getResources()
|
|
||||||
.getStringArray(R.array.multiuser_summary)[mm.multiuserMode]);
|
|
||||||
namespaceMode.setSummary(getResources()
|
|
||||||
.getStringArray(R.array.namespace_summary)[mm.suNamespaceMode]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTopicPublished(Topic topic) {
|
|
||||||
setLocalePreference((ListPreference) findPreference(Const.Key.LOCALE));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Topic[] getSubscription() {
|
|
||||||
return new Topic[] { mm.localeDone };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -7,33 +7,34 @@ import android.os.Build;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
||||||
import com.topjohnwu.magisk.asyncs.LoadModules;
|
|
||||||
import com.topjohnwu.magisk.asyncs.ParallelTask;
|
|
||||||
import com.topjohnwu.magisk.asyncs.UpdateRepos;
|
import com.topjohnwu.magisk.asyncs.UpdateRepos;
|
||||||
import com.topjohnwu.magisk.components.Activity;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
||||||
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
|
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Download;
|
||||||
import com.topjohnwu.magisk.utils.RootUtils;
|
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
public class SplashActivity extends Activity {
|
public class SplashActivity extends BaseActivity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
RootUtils.init();
|
// Magisk working as expected
|
||||||
MagiskManager mm = getMagiskManager();
|
if (Shell.rootAccess() && Data.magiskVersionCode > 0) {
|
||||||
|
// Update check service
|
||||||
|
Utils.setupUpdateCheck();
|
||||||
|
// Load modules
|
||||||
|
Utils.loadModules();
|
||||||
|
}
|
||||||
|
|
||||||
mm.repoDB = new RepoDatabaseHelper(this);
|
mm.repoDB = new RepoDatabaseHelper(this);
|
||||||
mm.loadMagiskInfo();
|
Data.importPrefs();
|
||||||
mm.getDefaultInstallFlags();
|
|
||||||
mm.loadPrefs();
|
|
||||||
|
|
||||||
// Dynamic detect all locales
|
// Dynamic detect all locales
|
||||||
new LoadLocale().exec();
|
LocaleManager.loadAvailableLocales();
|
||||||
|
|
||||||
// Create notification channel on Android O
|
// Create notification channel on Android O
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
@@ -43,46 +44,24 @@ public class SplashActivity extends Activity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Setup shortcuts
|
// Setup shortcuts
|
||||||
sendBroadcast(new Intent(this, ShortcutReceiver.class));
|
sendBroadcast(new Intent(this, Data.classMap.get(ShortcutReceiver.class)));
|
||||||
|
|
||||||
LoadModules loadModuleTask = new LoadModules();
|
if (Download.checkNetworkStatus(this)) {
|
||||||
|
|
||||||
if (Utils.checkNetworkStatus()) {
|
|
||||||
// Fire update check
|
// Fire update check
|
||||||
new CheckUpdates().exec();
|
CheckUpdates.check();
|
||||||
// Add repo update check
|
// Repo update check
|
||||||
loadModuleTask.setCallBack(() -> new UpdateRepos(false).exec());
|
new UpdateRepos().exec();
|
||||||
}
|
|
||||||
|
|
||||||
// Magisk working as expected
|
|
||||||
if (Shell.rootAccess() && mm.magiskVersionCode > 0) {
|
|
||||||
// Update check service
|
|
||||||
mm.setupUpdateCheck();
|
|
||||||
// Fire asynctasks
|
|
||||||
loadModuleTask.exec();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write back default values
|
// Write back default values
|
||||||
mm.writeConfig();
|
Data.writeConfig();
|
||||||
|
|
||||||
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(Const.Key.INTENT_PERM, getIntent().getStringExtra(Const.Key.INTENT_PERM));
|
intent.putExtra(BaseActivity.INTENT_PERM, getIntent().getStringExtra(BaseActivity.INTENT_PERM));
|
||||||
startActivity(intent);
|
startActivity(intent);
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
static class LoadLocale extends ParallelTask<Void, Void, Void> {
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... voids) {
|
|
||||||
MagiskManager.get().locales = Utils.getAvailableLocale();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void aVoid) {
|
|
||||||
MagiskManager.get().localeDone.publish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
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.DataOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
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 response() {
|
||||||
|
try (OutputStream out = getOutputStream()) {
|
||||||
|
out.write((policy.policy == Policy.ALLOW ? "socket:ALLOW" : "socket:DENY").getBytes());
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SuConnectorV2 extends SuConnector {
|
||||||
|
|
||||||
|
SuConnectorV2(String name) throws IOException {
|
||||||
|
socket.connect(new LocalSocketAddress(name, LocalSocketAddress.Namespace.ABSTRACT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void response() {
|
||||||
|
try (DataOutputStream out = getOutputStream()) {
|
||||||
|
out.writeInt(policy.policy);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 {
|
||||||
|
connector = intent.getIntExtra("version", 1) == 1 ?
|
||||||
|
new SuConnectorV1(intent.getStringExtra("socket")) :
|
||||||
|
new SuConnectorV2(intent.getStringExtra("socket"));
|
||||||
|
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, Const.ORIG_PKG_NAME)) {
|
||||||
|
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 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());
|
||||||
|
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.addPolicy(policy);
|
||||||
|
}
|
||||||
|
handleAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
package com.topjohnwu.magisk.adapters;
|
package com.topjohnwu.magisk.adapters;
|
||||||
|
|
||||||
|
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.support.v7.widget.RecyclerView;
|
import android.os.AsyncTask;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -12,10 +13,10 @@ import android.widget.Filter;
|
|||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.asyncs.ParallelTask;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -23,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> {
|
||||||
|
|
||||||
@@ -33,36 +35,61 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
private PackageManager pm;
|
private PackageManager pm;
|
||||||
private ApplicationFilter filter;
|
private ApplicationFilter filter;
|
||||||
|
|
||||||
public ApplicationAdapter() {
|
public ApplicationAdapter(Context context) {
|
||||||
fullList = showList = Collections.emptyList();
|
fullList = showList = Collections.emptyList();
|
||||||
hideList = Collections.emptyList();
|
hideList = Collections.emptyList();
|
||||||
filter = new ApplicationFilter();
|
filter = new ApplicationFilter();
|
||||||
pm = MagiskManager.get().getPackageManager();
|
pm = context.getPackageManager();
|
||||||
new LoadApps().exec();
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(this::loadApps);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
|
||||||
|
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false);
|
||||||
|
return new ViewHolder(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadApps() {
|
||||||
|
fullList = pm.getInstalledApplications(0);
|
||||||
|
hideList = Shell.su("magiskhide --ls").exec().getOut();
|
||||||
|
for (Iterator<ApplicationInfo> i = fullList.iterator(); i.hasNext(); ) {
|
||||||
|
ApplicationInfo info = i.next();
|
||||||
|
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled || info.uid == 1000) {
|
||||||
|
i.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Collections.sort(fullList, (a, b) -> {
|
||||||
|
boolean ah = hideList.contains(a.packageName);
|
||||||
|
boolean bh = hideList.contains(b.packageName);
|
||||||
|
if (ah == bh) {
|
||||||
|
return Utils.getAppLabel(a, pm).toLowerCase()
|
||||||
|
.compareTo(Utils.getAppLabel(b, pm).toLowerCase());
|
||||||
|
} else if (ah) {
|
||||||
|
return -1;
|
||||||
|
} else {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
Topic.publish(false, Topic.MAGISK_HIDE_DONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
|
||||||
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false);
|
|
||||||
return new ViewHolder(mView);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBindViewHolder(final ViewHolder holder, int position) {
|
|
||||||
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(info.loadLabel(pm));
|
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);
|
||||||
holder.checkBox.setChecked(hideList.contains(info.packageName));
|
holder.checkBox.setChecked(hideList.contains(info.packageName));
|
||||||
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
|
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
Shell.Async.su("magiskhide --add " + info.packageName);
|
Shell.su("magiskhide --add " + info.packageName).submit();
|
||||||
hideList.add(info.packageName);
|
hideList.add(info.packageName);
|
||||||
} else {
|
} else {
|
||||||
Shell.Async.su("magiskhide --rm " + info.packageName);
|
Shell.su("magiskhide --rm " + info.packageName).submit();
|
||||||
hideList.remove(info.packageName);
|
hideList.remove(info.packageName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -78,7 +105,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void refresh() {
|
public void refresh() {
|
||||||
new LoadApps().exec();
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(this::loadApps);
|
||||||
}
|
}
|
||||||
|
|
||||||
static class ViewHolder extends RecyclerView.ViewHolder {
|
static class ViewHolder extends RecyclerView.ViewHolder {
|
||||||
@@ -90,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);
|
||||||
@@ -108,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(info.loadLabel(pm).toString(), filter)
|
if (lowercaseContains(Utils.getAppLabel(info, pm), filter)
|
||||||
|| lowercaseContains(info.packageName, filter)) {
|
|| lowercaseContains(info.packageName, filter)) {
|
||||||
showList.add(info);
|
showList.add(info);
|
||||||
}
|
}
|
||||||
@@ -122,37 +149,4 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
|||||||
notifyDataSetChanged();
|
notifyDataSetChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class LoadApps extends ParallelTask<Void, Void, Void> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... voids) {
|
|
||||||
fullList = pm.getInstalledApplications(0);
|
|
||||||
hideList = Shell.Sync.su("magiskhide --ls");
|
|
||||||
for (Iterator<ApplicationInfo> i = fullList.iterator(); i.hasNext(); ) {
|
|
||||||
ApplicationInfo info = i.next();
|
|
||||||
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled) {
|
|
||||||
i.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Collections.sort(fullList, (a, b) -> {
|
|
||||||
boolean ah = hideList.contains(a.packageName);
|
|
||||||
boolean bh = hideList.contains(b.packageName);
|
|
||||||
if (ah == bh) {
|
|
||||||
return a.loadLabel(pm).toString().toLowerCase().compareTo(
|
|
||||||
b.loadLabel(pm).toString().toLowerCase());
|
|
||||||
} else if (ah) {
|
|
||||||
return -1;
|
|
||||||
} else {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void v) {
|
|
||||||
MagiskManager.get().magiskHideDone.publish(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,19 +9,23 @@ 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.Data;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
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.MagiskDatabaseHelper;
|
||||||
|
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> {
|
||||||
|
|
||||||
@@ -38,6 +40,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
|
|||||||
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 +66,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 (Data.suFingerprint) {
|
||||||
|
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) -> {
|
||||||
@@ -93,7 +116,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
|
|||||||
dbHelper.updatePolicy(policy);
|
dbHelper.updatePolicy(policy);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
holder.delete.setOnClickListener(v -> new AlertDialogBuilder((Activity) v.getContext())
|
holder.delete.setOnClickListener(v -> new CustomAlertDialog((Activity) v.getContext())
|
||||||
.setTitle(R.string.su_revoke_title)
|
.setTitle(R.string.su_revoke_title)
|
||||||
.setMessage(v.getContext().getString(R.string.su_revoke_msg, policy.appName))
|
.setMessage(v.getContext().getString(R.string.su_revoke_msg, policy.appName))
|
||||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||||
@@ -107,9 +130,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 +157,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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
package com.topjohnwu.magisk.adapters;
|
package com.topjohnwu.magisk.adapters;
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
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;
|
||||||
@@ -16,18 +14,18 @@ import android.widget.TextView;
|
|||||||
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.asyncs.ProcessRepoZip;
|
import com.topjohnwu.magisk.asyncs.ProcessRepoZip;
|
||||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
|
import com.topjohnwu.magisk.components.CustomAlertDialog;
|
||||||
import com.topjohnwu.magisk.container.Module;
|
import com.topjohnwu.magisk.container.Module;
|
||||||
import com.topjohnwu.magisk.container.Repo;
|
import com.topjohnwu.magisk.container.Repo;
|
||||||
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
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> {
|
||||||
|
|
||||||
@@ -90,29 +88,31 @@ 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 ->
|
||||||
new MarkDownWindow((Activity) context, null, repo.getDetailUrl()).exec());
|
new MarkDownWindow((BaseActivity) context, null, repo.getDetailUrl()).exec());
|
||||||
|
|
||||||
holder.downloadImage.setOnClickListener(v -> {
|
holder.downloadImage.setOnClickListener(v -> {
|
||||||
String filename = repo.getName() + "-" + repo.getVersion() + ".zip";
|
new CustomAlertDialog((BaseActivity) context)
|
||||||
new AlertDialogBuilder((Activity) 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, filename))
|
.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((Activity) context, repo.getZipUrl(),
|
new ProcessRepoZip((BaseActivity) context, repo, true).exec()
|
||||||
Utils.getLegalFilename(filename), true).exec()
|
|
||||||
)
|
)
|
||||||
.setNeutralButton(R.string.download, (d, i) ->
|
.setNeutralButton(R.string.download, (d, i) ->
|
||||||
new ProcessRepoZip((Activity) context, repo.getZipUrl(),
|
new ProcessRepoZip((BaseActivity) context, repo, false).exec())
|
||||||
Utils.getLegalFilename(filename), false).exec())
|
|
||||||
.setNegativeButton(R.string.no_thanks, null)
|
.setNegativeButton(R.string.no_thanks, null)
|
||||||
.show();
|
.show();
|
||||||
});
|
});
|
||||||
@@ -169,7 +169,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +185,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,8 +1,9 @@
|
|||||||
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.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> {
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.topjohnwu.magisk.adapters;
|
package com.topjohnwu.magisk.adapters;
|
||||||
|
|
||||||
import android.database.Cursor;
|
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;
|
||||||
@@ -20,8 +19,8 @@ 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> {
|
||||||
|
|
||||||
@@ -124,7 +123,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 +141,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,9 +2,9 @@ package com.topjohnwu.magisk.asyncs;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
|
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
|
||||||
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
@@ -19,10 +19,10 @@ import java.net.HttpURLConnection;
|
|||||||
|
|
||||||
import dalvik.system.DexClassLoader;
|
import dalvik.system.DexClassLoader;
|
||||||
|
|
||||||
public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
|
public class CheckSafetyNet extends ParallelTask<Void, Void, Void> {
|
||||||
|
|
||||||
public static final File dexPath =
|
public static final File dexPath =
|
||||||
new File(MagiskManager.get().getFilesDir().getParent() + "/snet", "snet.apk");
|
new File(Data.MM().getFilesDir().getParent() + "/snet", "snet.apk");
|
||||||
private ISafetyNetHelper helper;
|
private ISafetyNetHelper helper;
|
||||||
|
|
||||||
public CheckSafetyNet(Activity activity) {
|
public CheckSafetyNet(Activity activity) {
|
||||||
@@ -30,9 +30,9 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void dlSnet() throws Exception {
|
private void dlSnet() throws Exception {
|
||||||
Shell.Sync.sh("rm -rf " + dexPath.getParent());
|
Shell.sh("rm -rf " + dexPath.getParent()).exec();
|
||||||
dexPath.getParentFile().mkdir();
|
dexPath.getParentFile().mkdir();
|
||||||
HttpURLConnection conn = WebService.request(Const.Url.SNET_URL, null);
|
HttpURLConnection conn = WebService.mustRequest(Data.snetLink, null);
|
||||||
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())) {
|
||||||
@@ -45,17 +45,19 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
|
|||||||
private void dyload() throws Exception {
|
private void dyload() throws Exception {
|
||||||
DexClassLoader loader = new DexClassLoader(dexPath.getPath(), dexPath.getParent(),
|
DexClassLoader loader = new DexClassLoader(dexPath.getPath(), dexPath.getParent(),
|
||||||
null, ISafetyNetHelper.class.getClassLoader());
|
null, ISafetyNetHelper.class.getClassLoader());
|
||||||
Class<?> clazz = loader.loadClass("com.topjohnwu.snet.SafetyNetHelper");
|
Class<?> clazz = loader.loadClass("com.topjohnwu.snet.Snet");
|
||||||
helper = (ISafetyNetHelper) clazz.getConstructors()[0]
|
helper = (ISafetyNetHelper) clazz.getMethod("newHelper",
|
||||||
.newInstance(getActivity(), (ISafetyNetHelper.Callback)
|
Class.class, String.class, Activity.class, Object.class)
|
||||||
code -> MagiskManager.get().safetyNetDone.publish(false, code));
|
.invoke(null, ISafetyNetHelper.class, dexPath.getPath(), getActivity(),
|
||||||
if (helper.getVersion() != Const.SNET_VER) {
|
(ISafetyNetHelper.Callback) code ->
|
||||||
|
Topic.publish(false, Topic.SNET_CHECK_DONE, code));
|
||||||
|
if (helper.getVersion() < Data.snetVersionCode) {
|
||||||
throw new Exception();
|
throw new Exception();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Exception doInBackground(Void... voids) {
|
protected Void doInBackground(Void... voids) {
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
dyload();
|
dyload();
|
||||||
@@ -64,21 +66,12 @@ public class CheckSafetyNet extends ParallelTask<Void, Void, Exception> {
|
|||||||
dlSnet();
|
dlSnet();
|
||||||
dyload();
|
dyload();
|
||||||
}
|
}
|
||||||
|
// Run attestation
|
||||||
|
helper.attest();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return e;
|
e.printStackTrace();
|
||||||
|
Topic.publish(false, Topic.SNET_CHECK_DONE, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Exception e) {
|
|
||||||
if (e == null) {
|
|
||||||
helper.attest();
|
|
||||||
} else {
|
|
||||||
e.printStackTrace();
|
|
||||||
MagiskManager.get().safetyNetDone.publish(false, -1);
|
|
||||||
}
|
|
||||||
super.onPostExecute(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,31 +1,50 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.BuildConfig;
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.utils.ShowUI;
|
import com.topjohnwu.magisk.utils.NotificationMgr;
|
||||||
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
|
|
||||||
import org.json.JSONException;
|
import org.json.JSONException;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
public class CheckUpdates extends ParallelTask<Void, Void, Void> {
|
public class CheckUpdates {
|
||||||
|
|
||||||
private boolean showNotification;
|
private static int getInt(JSONObject json, String name, int defValue) {
|
||||||
|
if (json == null)
|
||||||
public CheckUpdates() {
|
return defValue;
|
||||||
this(false);
|
try {
|
||||||
|
return json.getInt(name);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
return defValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public CheckUpdates(boolean b) {
|
private static String getString(JSONObject json, String name, String defValue) {
|
||||||
showNotification = b;
|
if (json == null)
|
||||||
|
return defValue;
|
||||||
|
try {
|
||||||
|
return json.getString(name);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
return defValue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static JSONObject getJson(JSONObject json, String name) {
|
||||||
protected Void doInBackground(Void... voids) {
|
try {
|
||||||
MagiskManager mm = MagiskManager.get();
|
return json.getJSONObject(name);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void fetchUpdates() {
|
||||||
String jsonStr = "";
|
String jsonStr = "";
|
||||||
switch (mm.updateChannel) {
|
switch (Data.updateChannel) {
|
||||||
case Const.Value.STABLE_CHANNEL:
|
case Const.Value.STABLE_CHANNEL:
|
||||||
jsonStr = WebService.getString(Const.Url.STABLE_URL);
|
jsonStr = WebService.getString(Const.Url.STABLE_URL);
|
||||||
break;
|
break;
|
||||||
@@ -33,38 +52,54 @@ public class CheckUpdates extends ParallelTask<Void, Void, Void> {
|
|||||||
jsonStr = WebService.getString(Const.Url.BETA_URL);
|
jsonStr = WebService.getString(Const.Url.BETA_URL);
|
||||||
break;
|
break;
|
||||||
case Const.Value.CUSTOM_CHANNEL:
|
case Const.Value.CUSTOM_CHANNEL:
|
||||||
jsonStr = WebService.getString(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
|
jsonStr = WebService.getString(Data.MM().prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSONObject json;
|
||||||
try {
|
try {
|
||||||
JSONObject json = new JSONObject(jsonStr);
|
json = new JSONObject(jsonStr);
|
||||||
JSONObject magisk = json.getJSONObject("magisk");
|
} catch (JSONException e) {
|
||||||
mm.remoteMagiskVersionString = magisk.getString("version");
|
return;
|
||||||
mm.remoteMagiskVersionCode = magisk.getInt("versionCode");
|
}
|
||||||
mm.magiskLink = magisk.getString("link");
|
|
||||||
mm.magiskNoteLink = magisk.getString("note");
|
JSONObject magisk = getJson(json, "magisk");
|
||||||
JSONObject manager = json.getJSONObject("app");
|
Data.remoteMagiskVersionString = getString(magisk, "version", null);
|
||||||
mm.remoteManagerVersionString = manager.getString("version");
|
Data.remoteMagiskVersionCode = getInt(magisk, "versionCode", -1);
|
||||||
mm.remoteManagerVersionCode = manager.getInt("versionCode");
|
Data.magiskLink = getString(magisk, "link", null);
|
||||||
mm.managerLink = manager.getString("link");
|
Data.magiskNoteLink = getString(magisk, "note", null);
|
||||||
mm.managerNoteLink = manager.getString("note");
|
Data.magiskMD5 = getString(magisk, "md5", null);
|
||||||
JSONObject uninstaller = json.getJSONObject("uninstaller");
|
|
||||||
mm.uninstallerLink = uninstaller.getString("link");
|
JSONObject manager = getJson(json, "app");
|
||||||
} catch (JSONException ignored) {}
|
Data.remoteManagerVersionString = getString(manager, "version", null);
|
||||||
return 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public static void check(Runnable cb) {
|
||||||
protected void onPostExecute(Void v) {
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
MagiskManager mm = MagiskManager.get();
|
fetchUpdates();
|
||||||
if (showNotification) {
|
if (cb != null) {
|
||||||
if (BuildConfig.VERSION_CODE < mm.remoteManagerVersionCode) {
|
if (BuildConfig.VERSION_CODE < Data.remoteManagerVersionCode) {
|
||||||
ShowUI.managerUpdateNotification();
|
NotificationMgr.managerUpdate();
|
||||||
} else if (mm.magiskVersionCode < mm.remoteMagiskVersionCode) {
|
} else if (Data.magiskVersionCode < Data.remoteMagiskVersionCode) {
|
||||||
ShowUI.magiskUpdateNotification();
|
NotificationMgr.magiskUpdate();
|
||||||
|
}
|
||||||
|
cb.run();
|
||||||
}
|
}
|
||||||
}
|
Topic.publish(Topic.UPDATE_CHECK_DONE);
|
||||||
mm.updateCheckDone.publish();
|
});
|
||||||
super.onPostExecute(v);
|
}
|
||||||
|
|
||||||
|
public static void check() {
|
||||||
|
check(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ package com.topjohnwu.magisk.asyncs;
|
|||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.FlashActivity;
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
@@ -45,7 +45,7 @@ public class FlashZip extends ParallelTask<Void, Void, Integer> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Integer doInBackground(Void... voids) {
|
protected Integer doInBackground(Void... voids) {
|
||||||
MagiskManager mm = MagiskManager.get();
|
MagiskManager mm = Data.MM();
|
||||||
try {
|
try {
|
||||||
console.add("- Copying zip to temp directory");
|
console.add("- Copying zip to temp directory");
|
||||||
|
|
||||||
@@ -66,12 +66,10 @@ public class FlashZip extends ParallelTask<Void, Void, Integer> {
|
|||||||
}
|
}
|
||||||
if (!unzipAndCheck()) return 0;
|
if (!unzipAndCheck()) return 0;
|
||||||
console.add("- Installing " + Utils.getNameFromUri(mm, mUri));
|
console.add("- Installing " + Utils.getNameFromUri(mm, mUri));
|
||||||
Shell.Sync.su(console, logs,
|
if (!Shell.su("cd " + mCachedFile.getParent(),
|
||||||
"cd " + mCachedFile.getParent(),
|
"BOOTMODE=true sh update-binary dummy 1 " + mCachedFile)
|
||||||
"BOOTMODE=true sh update-binary dummy 1 " + mCachedFile + " || echo 'Failed!'"
|
.to(console, logs)
|
||||||
);
|
.exec().isSuccess())
|
||||||
|
|
||||||
if (TextUtils.equals(console.get(console.size() - 1), "Failed!"))
|
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -86,10 +84,7 @@ public class FlashZip extends ParallelTask<Void, Void, Integer> {
|
|||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Integer result) {
|
protected void onPostExecute(Integer result) {
|
||||||
FlashActivity activity = (FlashActivity) getActivity();
|
FlashActivity activity = (FlashActivity) getActivity();
|
||||||
Shell.Async.su(
|
Shell.su("rm -rf " + mCachedFile.getParent(), "rm -rf " + Const.TMP_FOLDER_PATH).submit();
|
||||||
"rm -rf " + mCachedFile.getParent(),
|
|
||||||
"rm -rf " + Const.TMP_FOLDER_PATH
|
|
||||||
);
|
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case -1:
|
case -1:
|
||||||
console.add("! Installation failed");
|
console.add("! Installation failed");
|
||||||
@@ -99,8 +94,8 @@ public class FlashZip extends ParallelTask<Void, Void, Integer> {
|
|||||||
console.add("! This zip is not a Magisk Module!");
|
console.add("! This zip is not a Magisk Module!");
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
// Success
|
// Reload modules
|
||||||
new LoadModules().exec();
|
Utils.loadModules();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
activity.reboot.setVisibility(result > 0 ? View.VISIBLE : View.GONE);
|
activity.reboot.setVisibility(result > 0 ? View.VISIBLE : View.GONE);
|
||||||
|
|||||||
@@ -1,95 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
import com.topjohnwu.magisk.utils.PatchAPK;
|
|
||||||
import com.topjohnwu.magisk.utils.RootUtils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
|
||||||
import com.topjohnwu.superuser.io.SuFileOutputStream;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
|
|
||||||
public class HideManager extends ParallelTask<Void, Void, Boolean> {
|
|
||||||
|
|
||||||
private ProgressDialog dialog;
|
|
||||||
|
|
||||||
public HideManager(Activity activity) {
|
|
||||||
super(activity);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String genPackageName(String prefix, int length) {
|
|
||||||
StringBuilder builder = new StringBuilder(length);
|
|
||||||
builder.append(prefix);
|
|
||||||
length -= prefix.length();
|
|
||||||
SecureRandom random = new SecureRandom();
|
|
||||||
String base = "abcdefghijklmnopqrstuvwxyz";
|
|
||||||
String alpha = base + base.toUpperCase();
|
|
||||||
String full = alpha + "0123456789..........";
|
|
||||||
char next, prev = '\0';
|
|
||||||
for (int i = 0; i < length; ++i) {
|
|
||||||
if (prev == '.' || i == length - 1 || i == 0) {
|
|
||||||
next = alpha.charAt(random.nextInt(alpha.length()));
|
|
||||||
} else {
|
|
||||||
next = full.charAt(random.nextInt(full.length()));
|
|
||||||
}
|
|
||||||
builder.append(next);
|
|
||||||
prev = next;
|
|
||||||
}
|
|
||||||
return builder.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
dialog = ProgressDialog.show(getActivity(),
|
|
||||||
getActivity().getString(R.string.hide_manager_toast),
|
|
||||||
getActivity().getString(R.string.hide_manager_toast2));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Boolean doInBackground(Void... voids) {
|
|
||||||
MagiskManager mm = MagiskManager.get();
|
|
||||||
|
|
||||||
// Generate a new app with random package name
|
|
||||||
SuFile repack = new SuFile("/data/local/tmp/repack.apk");
|
|
||||||
String pkg = genPackageName("com.", Const.ORIG_PKG_NAME.length());
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!PatchAPK.patchPackageID(
|
|
||||||
mm.getPackageCodePath(),
|
|
||||||
new SuFileOutputStream(repack),
|
|
||||||
Const.ORIG_PKG_NAME, pkg))
|
|
||||||
return false;
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Install the application
|
|
||||||
if (!ShellUtils.fastCmdResult(Shell.getShell(), "pm install " + repack))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
repack.delete();
|
|
||||||
|
|
||||||
mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
|
|
||||||
mm.dumpPrefs();
|
|
||||||
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean b) {
|
|
||||||
dialog.dismiss();
|
|
||||||
if (!b) {
|
|
||||||
MagiskManager.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
|
|
||||||
}
|
|
||||||
super.onPostExecute(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -8,16 +8,22 @@ import android.text.TextUtils;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.FlashActivity;
|
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.Const;
|
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.ZipUtils;
|
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
import com.topjohnwu.superuser.internal.NOPList;
|
||||||
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
import com.topjohnwu.superuser.io.SuFileInputStream;
|
import com.topjohnwu.superuser.io.SuFileInputStream;
|
||||||
|
import com.topjohnwu.superuser.io.SuFileOutputStream;
|
||||||
import com.topjohnwu.utils.SignBoot;
|
import com.topjohnwu.utils.SignBoot;
|
||||||
|
|
||||||
import org.kamranzafar.jtar.TarInputStream;
|
import org.kamranzafar.jtar.TarInputStream;
|
||||||
@@ -26,15 +32,19 @@ import org.kamranzafar.jtar.TarOutputStream;
|
|||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.FilterInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.util.AbstractList;
|
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;
|
||||||
@@ -42,7 +52,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
private static final int FIX_ENV_MODE = 2;
|
private static final int FIX_ENV_MODE = 2;
|
||||||
public static final int SECOND_SLOT_MODE = 3;
|
public static final int SECOND_SLOT_MODE = 3;
|
||||||
|
|
||||||
private Uri bootUri, mZip;
|
private Uri bootUri;
|
||||||
private List<String> console, logs;
|
private List<String> console, logs;
|
||||||
private String mBoot;
|
private String mBoot;
|
||||||
private int mode;
|
private int mode;
|
||||||
@@ -50,22 +60,21 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
private ProgressDialog dialog;
|
private ProgressDialog dialog;
|
||||||
private MagiskManager mm;
|
private MagiskManager mm;
|
||||||
|
|
||||||
public InstallMagisk(Activity context, Uri zip) {
|
public InstallMagisk(Activity context) {
|
||||||
super(context);
|
super(context);
|
||||||
mZip = zip;
|
mm = Data.MM();
|
||||||
mm = MagiskManager.get();
|
|
||||||
mode = FIX_ENV_MODE;
|
mode = FIX_ENV_MODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstallMagisk(Activity context, List<String> console, List<String> logs, Uri zip, int mode) {
|
public InstallMagisk(Activity context, List<String> console, List<String> logs, int mode) {
|
||||||
this(context, zip);
|
this(context);
|
||||||
this.console = console;
|
this.console = console;
|
||||||
this.logs = logs;
|
this.logs = logs;
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
public InstallMagisk(FlashActivity context, List<String> console, List<String> logs, Uri zip, Uri boot) {
|
public InstallMagisk(FlashActivity context, List<String> console, List<String> logs, Uri boot) {
|
||||||
this(context, console, logs, zip, PATCH_MODE);
|
this(context, console, logs, PATCH_MODE);
|
||||||
bootUri = boot;
|
bootUri = boot;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,37 +83,92 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
if (mode == FIX_ENV_MODE) {
|
if (mode == FIX_ENV_MODE) {
|
||||||
Activity a = getActivity();
|
Activity a = getActivity();
|
||||||
dialog = ProgressDialog.show(a, a.getString(R.string.setup_title), a.getString(R.string.setup_msg));
|
dialog = ProgressDialog.show(a, a.getString(R.string.setup_title), a.getString(R.string.setup_msg));
|
||||||
console = new NOPList<>();
|
console = NOPList.getInstance();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ProgressStream extends FilterInputStream {
|
||||||
|
|
||||||
|
private int prev = -1;
|
||||||
|
private int progress = 0;
|
||||||
|
private int total;
|
||||||
|
|
||||||
|
private ProgressStream(HttpURLConnection conn) throws IOException {
|
||||||
|
super(conn.getInputStream());
|
||||||
|
total = conn.getContentLength();
|
||||||
|
console.add("... 0%");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void update(int step) {
|
||||||
|
progress += step;
|
||||||
|
int curr = (int) (100 * (double) progress / total);
|
||||||
|
if (prev != curr) {
|
||||||
|
prev = curr;
|
||||||
|
console.set(console.size() - 1, "... " + prev + "%");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
int b = super.read();
|
||||||
|
if (b > 0)
|
||||||
|
update(1);
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(@NonNull byte[] b) throws IOException {
|
||||||
|
return read(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(@NonNull byte[] b, int off, int len) throws IOException {
|
||||||
|
int step = super.read(b, off, len);
|
||||||
|
if (step > 0)
|
||||||
|
update(step);
|
||||||
|
return step;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void extractFiles(String arch) throws IOException {
|
private void extractFiles(String arch) throws IOException {
|
||||||
|
File zip = new File(mm.getFilesDir(), "magisk.zip");
|
||||||
|
BufferedInputStream buf;
|
||||||
|
|
||||||
|
if (!ShellUtils.checkSum("MD5", zip, Data.magiskMD5)) {
|
||||||
|
console.add("- Downloading zip");
|
||||||
|
HttpURLConnection conn = WebService.mustRequest(Data.magiskLink, null);
|
||||||
|
buf = new BufferedInputStream(new ProgressStream(conn), conn.getContentLength());
|
||||||
|
buf.mark(conn.getContentLength() + 1);
|
||||||
|
try (OutputStream out = new FileOutputStream(zip)) {
|
||||||
|
ShellUtils.pump(buf, out);
|
||||||
|
}
|
||||||
|
buf.reset();
|
||||||
|
conn.disconnect();
|
||||||
|
} else {
|
||||||
|
console.add("- Existing zip found");
|
||||||
|
buf = new BufferedInputStream(new FileInputStream(zip), (int) zip.length());
|
||||||
|
buf.mark((int) zip.length() + 1);
|
||||||
|
}
|
||||||
|
|
||||||
console.add("- Extracting files");
|
console.add("- Extracting files");
|
||||||
try (InputStream in = mm.getContentResolver().openInputStream(mZip)) {
|
try (InputStream in = buf) {
|
||||||
if (in == null) throw new FileNotFoundException();
|
ZipUtils.unzip(in, installDir, arch + "/", true);
|
||||||
BufferedInputStream buf = new BufferedInputStream(in);
|
in.reset();
|
||||||
buf.mark(Integer.MAX_VALUE);
|
ZipUtils.unzip(in, installDir, "common/", true);
|
||||||
ZipUtils.unzip(buf, installDir, arch + "/", true);
|
in.reset();
|
||||||
buf.reset();
|
ZipUtils.unzip(in, installDir, "chromeos/", false);
|
||||||
ZipUtils.unzip(buf, installDir, "common/", true);
|
in.reset();
|
||||||
buf.reset();
|
ZipUtils.unzip(in, installDir, "META-INF/com/google/android/update-binary", true);
|
||||||
ZipUtils.unzip(buf, installDir, "chromeos/", false);
|
|
||||||
buf.reset();
|
|
||||||
ZipUtils.unzip(buf, installDir, "META-INF/com/google/android/update-binary", true);
|
|
||||||
buf.close();
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
console.add("! Invalid Uri");
|
|
||||||
throw e;
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
console.add("! Cannot unzip zip");
|
console.add("! Cannot unzip zip");
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
Shell.Sync.sh(Utils.fmt("chmod -R 755 %s/*; %s/magiskinit -x magisk %s/magisk",
|
Shell.sh(Utils.fmt("chmod -R 755 %s/*; %s/magiskinit -x magisk %s/magisk",
|
||||||
installDir, installDir, installDir));
|
installDir, installDir, installDir)).exec();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean dumpBoot() {
|
private boolean dumpBoot() {
|
||||||
console.add("- Copying image locally");
|
console.add("- Copying image to cache");
|
||||||
// Copy boot image to local
|
// Copy boot image to local
|
||||||
try (InputStream in = mm.getContentResolver().openInputStream(bootUri);
|
try (InputStream in = mm.getContentResolver().openInputStream(bootUri);
|
||||||
OutputStream out = new FileOutputStream(mBoot)
|
OutputStream out = new FileOutputStream(mBoot)
|
||||||
@@ -150,16 +214,13 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Patch boot image
|
// Patch boot image
|
||||||
Shell.Sync.sh(console, logs,
|
if (!Shell.sh("cd " + installDir, Utils.fmt(
|
||||||
"cd " + installDir,
|
"KEEPFORCEENCRYPT=%b KEEPVERITY=%b sh update-binary indep boot_patch.sh %s",
|
||||||
Utils.fmt("KEEPFORCEENCRYPT=%b KEEPVERITY=%b sh update-binary indep " +
|
Data.keepEnc, Data.keepVerity, mBoot))
|
||||||
"boot_patch.sh %s || echo 'Failed!'",
|
.to(console, logs).exec().isSuccess())
|
||||||
mm.keepEnc, mm.keepVerity, mBoot));
|
|
||||||
|
|
||||||
if (TextUtils.equals(console.get(console.size() - 1), "Failed!"))
|
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
Shell.Sync.sh("mv bin/busybox busybox",
|
Shell.Job job = Shell.sh("mv bin/busybox busybox",
|
||||||
"rm -rf magisk.apk bin boot.img update-binary",
|
"rm -rf magisk.apk bin boot.img update-binary",
|
||||||
"cd /");
|
"cd /");
|
||||||
|
|
||||||
@@ -172,18 +233,20 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
) {
|
) {
|
||||||
SignBoot.doSignature("/boot", in, out, null, null);
|
SignBoot.doSignature("/boot", in, out, null, null);
|
||||||
}
|
}
|
||||||
Shell.Sync.su("mv -f " + signed + " " + patched);
|
job.add("mv -f " + signed + " " + patched);
|
||||||
}
|
}
|
||||||
|
job.exec();
|
||||||
return patched;
|
return patched;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void outputBoot(File patched) throws IOException {
|
private boolean outputBoot(File patched) throws IOException {
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case PATCH_MODE:
|
case PATCH_MODE:
|
||||||
File dest = new File(Const.EXTERNAL_PATH, "patched_boot" + mm.bootFormat);
|
String fmt = mm.prefs.getString(Const.Key.BOOT_FORMAT, ".img");
|
||||||
|
File dest = new File(Download.EXTERNAL_PATH, "patched_boot" + fmt);
|
||||||
dest.getParentFile().mkdirs();
|
dest.getParentFile().mkdirs();
|
||||||
OutputStream out;
|
OutputStream out;
|
||||||
switch (mm.bootFormat) {
|
switch (fmt) {
|
||||||
case ".img.tar":
|
case ".img.tar":
|
||||||
out = new TarOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
|
out = new TarOutputStream(new BufferedOutputStream(new FileOutputStream(dest)));
|
||||||
((TarOutputStream) out).putNextEntry(new TarEntry(patched, "boot.img"));
|
((TarOutputStream) out).putNextEntry(new TarEntry(patched, "boot.img"));
|
||||||
@@ -197,7 +260,7 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
ShellUtils.pump(in, out);
|
ShellUtils.pump(in, out);
|
||||||
out.close();
|
out.close();
|
||||||
}
|
}
|
||||||
Shell.Sync.su("rm -f " + patched);
|
Shell.sh("rm -f " + patched).exec();
|
||||||
console.add("");
|
console.add("");
|
||||||
console.add("****************************");
|
console.add("****************************");
|
||||||
console.add(" Patched image is placed in ");
|
console.add(" Patched image is placed in ");
|
||||||
@@ -206,26 +269,42 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
break;
|
break;
|
||||||
case SECOND_SLOT_MODE:
|
case SECOND_SLOT_MODE:
|
||||||
case DIRECT_MODE:
|
case DIRECT_MODE:
|
||||||
Shell.Sync.sh(console, logs,
|
if (!Shell.su(Utils.fmt("direct_install %s %s", installDir, mBoot))
|
||||||
Utils.fmt("direct_install %s %s %s", patched, mBoot, installDir));
|
.to(console, logs).exec().isSuccess())
|
||||||
if (!mm.keepVerity)
|
return false;
|
||||||
Shell.Sync.sh(console, logs, "find_dtbo_image", "patch_dtbo_image");
|
if (!Data.keepVerity)
|
||||||
|
Shell.su("find_dtbo_image", "patch_dtbo_image").to(console, logs).exec();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void postOTA() {
|
||||||
|
SuFile bootctl = new SuFile(Const.MAGISK_PATH + "/.core/bootctl");
|
||||||
|
try (InputStream in = mm.getResources().openRawResource(R.raw.bootctl);
|
||||||
|
OutputStream out = new SuFileOutputStream(bootctl)) {
|
||||||
|
ShellUtils.pump(in, out);
|
||||||
|
Shell.su("post_ota " + bootctl.getParent()).exec();
|
||||||
|
console.add("***************************************");
|
||||||
|
console.add(" Next reboot will boot to second slot!");
|
||||||
|
console.add("***************************************");
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(Void... voids) {
|
protected Boolean doInBackground(Void... voids) {
|
||||||
if (mode == FIX_ENV_MODE) {
|
if (mode == FIX_ENV_MODE) {
|
||||||
installDir = new File("/data/adb/magisk");
|
installDir = new File("/data/adb/magisk");
|
||||||
Shell.Sync.sh("rm -rf /data/adb/magisk/*");
|
Shell.su("rm -rf /data/adb/magisk/*").exec();
|
||||||
} else {
|
} else {
|
||||||
installDir = new File(
|
installDir = new File(
|
||||||
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ?
|
(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ?
|
||||||
mm.createDeviceProtectedStorageContext() : mm)
|
mm.createDeviceProtectedStorageContext() : mm)
|
||||||
.getFilesDir().getParent()
|
.getFilesDir().getParent()
|
||||||
, "install");
|
, "install");
|
||||||
Shell.Sync.sh("rm -rf " + installDir);
|
Shell.sh("rm -rf " + installDir).exec();
|
||||||
installDir.mkdirs();
|
installDir.mkdirs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,13 +319,16 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
mBoot = ShellUtils.fastCmd("find_boot_image", "echo \"$BOOTIMAGE\"");
|
mBoot = ShellUtils.fastCmd("find_boot_image", "echo \"$BOOTIMAGE\"");
|
||||||
break;
|
break;
|
||||||
case SECOND_SLOT_MODE:
|
case SECOND_SLOT_MODE:
|
||||||
|
String slot = ShellUtils.fastCmd("echo $SLOT");
|
||||||
|
String target = (TextUtils.equals(slot, "_a") ? "_b" : "_a");
|
||||||
|
console.add("- Target slot: " + target);
|
||||||
console.add("- Detecting target image");
|
console.add("- Detecting target image");
|
||||||
char slot[] = ShellUtils.fastCmd("echo $SLOT").toCharArray();
|
mBoot = ShellUtils.fastCmd(
|
||||||
if (slot[1] == 'a') slot[1] = 'b';
|
"SLOT=" + target,
|
||||||
else slot[1] = 'a';
|
"find_boot_image",
|
||||||
mBoot = ShellUtils.fastCmd("SLOT=" + String.valueOf(slot),
|
"SLOT=" + slot,
|
||||||
"find_boot_image", "echo \"$BOOTIMAGE\"");
|
"echo \"$BOOTIMAGE\""
|
||||||
Shell.Async.su("mount_partitions");
|
);
|
||||||
break;
|
break;
|
||||||
case FIX_ENV_MODE:
|
case FIX_ENV_MODE:
|
||||||
mBoot = "";
|
mBoot = "";
|
||||||
@@ -257,12 +339,13 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.add("- Target image: " + mBoot);
|
if (mode == DIRECT_MODE || mode == SECOND_SLOT_MODE)
|
||||||
|
console.add("- Target image: " + mBoot);
|
||||||
|
|
||||||
List<String> abis = Arrays.asList(Build.SUPPORTED_ABIS);
|
List<String> abis = Arrays.asList(Build.SUPPORTED_ABIS);
|
||||||
String arch;
|
String arch;
|
||||||
|
|
||||||
if (mm.remoteMagiskVersionCode >= Const.MAGISK_VER.SEPOL_REFACTOR) {
|
if (Data.remoteMagiskVersionCode >= Const.MAGISK_VER.SEPOL_REFACTOR) {
|
||||||
// 32-bit only
|
// 32-bit only
|
||||||
if (abis.contains("x86")) arch = "x86";
|
if (abis.contains("x86")) arch = "x86";
|
||||||
else arch = "arm";
|
else arch = "arm";
|
||||||
@@ -278,12 +361,15 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
try {
|
try {
|
||||||
extractFiles(arch);
|
extractFiles(arch);
|
||||||
if (mode == FIX_ENV_MODE) {
|
if (mode == FIX_ENV_MODE) {
|
||||||
Shell.Sync.sh("fix_env");
|
Shell.su("fix_env").exec();
|
||||||
} else {
|
} else {
|
||||||
File patched = patchBoot();
|
File patched = patchBoot();
|
||||||
if (patched == null)
|
if (patched == null)
|
||||||
return false;
|
return false;
|
||||||
outputBoot(patched);
|
if (!outputBoot(patched))
|
||||||
|
return false;
|
||||||
|
if (mode == SECOND_SLOT_MODE)
|
||||||
|
postOTA();
|
||||||
console.add("- All done!");
|
console.add("- All done!");
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -297,31 +383,16 @@ public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
|
|||||||
protected void onPostExecute(Boolean result) {
|
protected void onPostExecute(Boolean result) {
|
||||||
if (mode == FIX_ENV_MODE) {
|
if (mode == FIX_ENV_MODE) {
|
||||||
dialog.dismiss();
|
dialog.dismiss();
|
||||||
MagiskManager.toast(result ? R.string.setup_done : R.string.setup_fail, Toast.LENGTH_LONG);
|
Utils.toast(result ? R.string.setup_done : R.string.setup_fail, Toast.LENGTH_LONG);
|
||||||
} else {
|
} else {
|
||||||
// Running in FlashActivity
|
// Running in FlashActivity
|
||||||
FlashActivity activity = (FlashActivity) getActivity();
|
FlashActivity activity = (FlashActivity) getActivity();
|
||||||
if (!result) {
|
if (!result) {
|
||||||
Shell.Async.sh("rm -rf " + installDir);
|
Shell.sh("rm -rf " + installDir).submit();
|
||||||
console.add("! Installation failed");
|
console.add("! Installation failed");
|
||||||
activity.reboot.setVisibility(View.GONE);
|
activity.reboot.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
activity.buttonPanel.setVisibility(View.VISIBLE);
|
activity.buttonPanel.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class NOPList<E> extends AbstractList<E> {
|
|
||||||
@Override
|
|
||||||
public E get(int index) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void add(int index, E element) {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
|
||||||
import com.topjohnwu.magisk.container.Module;
|
|
||||||
import com.topjohnwu.magisk.container.ValueSortedMap;
|
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class LoadModules extends ParallelTask<Void, Void, Void> {
|
|
||||||
|
|
||||||
private List<String> getModList() {
|
|
||||||
String command = "ls -d " + Const.MAGISK_PATH + "/* | grep -v lost+found";
|
|
||||||
return Shell.Sync.su(command);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Void doInBackground(Void... voids) {
|
|
||||||
MagiskManager mm = MagiskManager.get();
|
|
||||||
mm.moduleMap = new ValueSortedMap<>();
|
|
||||||
|
|
||||||
for (String path : getModList()) {
|
|
||||||
Module module = new Module(path);
|
|
||||||
mm.moduleMap.put(module.getId(), module);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Void v) {
|
|
||||||
MagiskManager.get().moduleLoadDone.publish();
|
|
||||||
super.onPostExecute(v);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
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.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.WebService;
|
||||||
@@ -17,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;
|
||||||
@@ -38,7 +40,7 @@ public class MarkDownWindow extends ParallelTask<Void, Void, String> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String doInBackground(Void... voids) {
|
protected String doInBackground(Void... voids) {
|
||||||
MagiskManager mm = MagiskManager.get();
|
MagiskManager mm = Data.MM();
|
||||||
String md;
|
String md;
|
||||||
if (mUrl != null) {
|
if (mUrl != null) {
|
||||||
md = WebService.getString(mUrl);
|
md = WebService.getString(mUrl);
|
||||||
@@ -54,9 +56,9 @@ public class MarkDownWindow extends ParallelTask<Void, Void, String> {
|
|||||||
}
|
}
|
||||||
String css;
|
String css;
|
||||||
try (
|
try (
|
||||||
InputStream in = mm.getResources().openRawResource(
|
InputStream in = mm.getResources().openRawResource(
|
||||||
mm.isDarkTheme ? R.raw.dark : R.raw.light);
|
Data.isDarkTheme ? R.raw.dark : R.raw.light);
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream()
|
ByteArrayOutputStream out = new ByteArrayOutputStream()
|
||||||
) {
|
) {
|
||||||
ShellUtils.pump(in, out);
|
ShellUtils.pump(in, out);
|
||||||
css = out.toString();
|
css = out.toString();
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ public abstract class ParallelTask<Params, Progress, Result> extends AsyncTask<P
|
|||||||
|
|
||||||
private WeakReference<Activity> weakActivity;
|
private WeakReference<Activity> weakActivity;
|
||||||
|
|
||||||
private Runnable callback = null;
|
|
||||||
|
|
||||||
public ParallelTask() {}
|
public ParallelTask() {}
|
||||||
|
|
||||||
public ParallelTask(Activity context) {
|
public ParallelTask(Activity context) {
|
||||||
@@ -22,18 +20,7 @@ public abstract class ParallelTask<Params, Progress, Result> extends AsyncTask<P
|
|||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public ParallelTask<Params, Progress, Result> exec(Params... params) {
|
public void exec(Params... params) {
|
||||||
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
|
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Result result) {
|
|
||||||
if (callback != null) callback.run();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ParallelTask<Params, Progress, Result> setCallBack(Runnable next) {
|
|
||||||
callback = next;
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
154
app/src/full/java/com/topjohnwu/magisk/asyncs/PatchAPK.java
Normal file
154
app/src/full/java/com/topjohnwu/magisk/asyncs/PatchAPK.java
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
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.utils.RootUtils;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
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.SignAPK;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.jar.JarEntry;
|
||||||
|
|
||||||
|
public class PatchAPK {
|
||||||
|
|
||||||
|
private static String genPackageName(String prefix, int length) {
|
||||||
|
StringBuilder builder = new StringBuilder(length);
|
||||||
|
builder.append(prefix);
|
||||||
|
length -= prefix.length();
|
||||||
|
SecureRandom random = new SecureRandom();
|
||||||
|
String base = "abcdefghijklmnopqrstuvwxyz";
|
||||||
|
String alpha = base + base.toUpperCase();
|
||||||
|
String full = alpha + "0123456789..........";
|
||||||
|
char next, prev = '\0';
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
if (prev == '.' || i == length - 1 || i == 0) {
|
||||||
|
next = alpha.charAt(random.nextInt(alpha.length()));
|
||||||
|
} else {
|
||||||
|
next = full.charAt(random.nextInt(full.length()));
|
||||||
|
}
|
||||||
|
builder.append(next);
|
||||||
|
prev = next;
|
||||||
|
}
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int findOffset(byte buf[], byte pattern[]) {
|
||||||
|
int offset = -1;
|
||||||
|
for (int i = 0; i < buf.length - pattern.length; ++i) {
|
||||||
|
boolean match = true;
|
||||||
|
for (int j = 0; j < pattern.length; ++j) {
|
||||||
|
if (buf[i + j] != pattern[j]) {
|
||||||
|
match = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (match) {
|
||||||
|
offset = i;
|
||||||
|
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)
|
||||||
|
return false;
|
||||||
|
byte[] dest = new byte[target.length - 2];
|
||||||
|
for (int i = 0; i < to.length(); ++i) {
|
||||||
|
dest[i * 2] = (byte) to.charAt(i);
|
||||||
|
}
|
||||||
|
System.arraycopy(dest, 0, xml, offset, dest.length);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean findAndPatch(byte xml[], String from, String to) {
|
||||||
|
byte target[] = (from + '\0').getBytes();
|
||||||
|
int offset = findOffset(xml, target);
|
||||||
|
if (offset < 0)
|
||||||
|
return fallbackPatch(xml, from, to);
|
||||||
|
System.arraycopy(to.getBytes(), 0, xml, offset, to.length());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean patchAndHide() {
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
|
||||||
|
// Generate a new app with random package name
|
||||||
|
SuFile repack = new SuFile("/data/local/tmp/repack.apk");
|
||||||
|
String pkg = genPackageName("com.", Const.ORIG_PKG_NAME.length());
|
||||||
|
|
||||||
|
try {
|
||||||
|
JarMap apk = new JarMap(mm.getPackageCodePath());
|
||||||
|
if (!patchPackageID(apk, Const.ORIG_PKG_NAME, pkg))
|
||||||
|
return false;
|
||||||
|
SignAPK.sign(apk, new SuFileOutputStream(repack));
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install the application
|
||||||
|
if (!ShellUtils.fastCmdResult("pm install " + repack))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
repack.delete();
|
||||||
|
|
||||||
|
mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
|
||||||
|
mm.mDB.flush();
|
||||||
|
Data.exportPrefs();
|
||||||
|
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean patchPackageID(JarMap apk, String from, String to) {
|
||||||
|
try {
|
||||||
|
JarEntry je = apk.getJarEntry(Const.ANDROID_MANIFEST);
|
||||||
|
byte xml[] = apk.getRawData(je);
|
||||||
|
|
||||||
|
if (!findAndPatch(xml, from, to))
|
||||||
|
return false;
|
||||||
|
if (!findAndPatch(xml, from + ".provider", to + ".provider"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Write in changes
|
||||||
|
apk.getOutputStream(je).write(xml);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void hideManager(Activity activity) {
|
||||||
|
ProgressDialog dialog = ProgressDialog.show(activity,
|
||||||
|
activity.getString(R.string.hide_manager_toast),
|
||||||
|
activity.getString(R.string.hide_manager_toast2));
|
||||||
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
|
boolean b = patchAndHide();
|
||||||
|
Data.mainHandler.post(() -> {
|
||||||
|
dialog.cancel();
|
||||||
|
if (!b) {
|
||||||
|
Utils.toast(R.string.hide_manager_fail_toast, Toast.LENGTH_LONG);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +1,20 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
package com.topjohnwu.magisk.asyncs;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Handler;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
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.components.BaseActivity;
|
||||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
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.WebService;
|
||||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
@@ -33,21 +34,21 @@ import java.util.jar.JarEntry;
|
|||||||
import java.util.jar.JarInputStream;
|
import java.util.jar.JarInputStream;
|
||||||
import java.util.jar.JarOutputStream;
|
import java.util.jar.JarOutputStream;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
||||||
|
|
||||||
private ProgressDialog progressDialog;
|
private ProgressDialog progressDialog;
|
||||||
private boolean mInstall;
|
private boolean mInstall;
|
||||||
private String mLink;
|
|
||||||
private File mFile;
|
private File mFile;
|
||||||
|
private Repo mRepo;
|
||||||
private int progress = 0, total = -1;
|
private int progress = 0, total = -1;
|
||||||
private Handler mHandler;
|
|
||||||
|
|
||||||
public ProcessRepoZip(Activity context, String link, String filename, boolean install) {
|
public ProcessRepoZip(BaseActivity context, Repo repo, boolean install) {
|
||||||
super(context);
|
super(context);
|
||||||
mLink = link;
|
mRepo = repo;
|
||||||
mFile = new File(Const.EXTERNAL_PATH, filename);
|
mInstall = install && Shell.rootAccess();
|
||||||
mInstall = install;
|
mFile = new File(Download.EXTERNAL_PATH, repo.getDownloadFilename());
|
||||||
mHandler = new Handler();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeTopFolder(File input, File output) throws IOException {
|
private void removeTopFolder(File input, File output) throws IOException {
|
||||||
@@ -74,28 +75,26 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected BaseActivity getActivity() {
|
||||||
|
return (BaseActivity) super.getActivity();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPreExecute() {
|
protected void onPreExecute() {
|
||||||
Activity activity = getActivity();
|
BaseActivity activity = getActivity();
|
||||||
mFile.getParentFile().mkdirs();
|
mFile.getParentFile().mkdirs();
|
||||||
progressDialog = ProgressDialog.show(activity, activity.getString(R.string.zip_download_title), activity.getString(R.string.zip_download_msg, 0));
|
progressDialog = ProgressDialog.show(activity, activity.getString(R.string.zip_download_title), activity.getString(R.string.zip_download_msg, 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Boolean doInBackground(Void... params) {
|
protected Boolean doInBackground(Void... params) {
|
||||||
Activity activity = getActivity();
|
BaseActivity activity = getActivity();
|
||||||
if (activity == null) return null;
|
if (activity == null) return null;
|
||||||
try {
|
try {
|
||||||
// Request zip from Internet
|
// Request zip from Internet
|
||||||
HttpURLConnection conn;
|
HttpURLConnection conn = WebService.mustRequest(mRepo.getZipUrl(), null);
|
||||||
do {
|
total = conn.getContentLength();
|
||||||
conn = WebService.request(mLink, null);
|
|
||||||
total = conn.getContentLength();
|
|
||||||
if (total < 0)
|
|
||||||
conn.disconnect();
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
} while (true);
|
|
||||||
|
|
||||||
// Temp files
|
// Temp files
|
||||||
File temp1 = new File(activity.getCacheDir(), "1.zip");
|
File temp1 = new File(activity.getCacheDir(), "1.zip");
|
||||||
@@ -112,7 +111,7 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|||||||
}
|
}
|
||||||
conn.disconnect();
|
conn.disconnect();
|
||||||
|
|
||||||
mHandler.post(() -> {
|
Data.mainHandler.post(() -> {
|
||||||
progressDialog.setTitle(R.string.zip_process_title);
|
progressDialog.setTitle(R.string.zip_process_title);
|
||||||
progressDialog.setMessage(getActivity().getString(R.string.zip_process_msg));
|
progressDialog.setMessage(getActivity().getString(R.string.zip_process_msg));
|
||||||
});
|
});
|
||||||
@@ -136,30 +135,28 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Boolean result) {
|
protected void onPostExecute(Boolean result) {
|
||||||
Activity activity = getActivity();
|
BaseActivity activity = getActivity();
|
||||||
if (activity == null) return;
|
if (activity == null) return;
|
||||||
progressDialog.dismiss();
|
progressDialog.dismiss();
|
||||||
if (result) {
|
if (result) {
|
||||||
Uri uri = Uri.fromFile(mFile);
|
Uri uri = Uri.fromFile(mFile);
|
||||||
if (Shell.rootAccess() && mInstall) {
|
if (mInstall) {
|
||||||
Intent intent = new Intent(activity, FlashActivity.class);
|
Intent intent = new Intent(activity, Data.classMap.get(FlashActivity.class));
|
||||||
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_ZIP);
|
||||||
activity.startActivity(intent);
|
activity.startActivity(intent);
|
||||||
} else {
|
} else {
|
||||||
SnackbarMaker.showUri(activity, uri);
|
SnackbarMaker.showUri(activity, uri);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MagiskManager.toast(R.string.process_error, Toast.LENGTH_LONG);
|
Utils.toast(R.string.process_error, Toast.LENGTH_LONG);
|
||||||
}
|
}
|
||||||
super.onPostExecute(result);
|
super.onPostExecute(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ParallelTask<Void, Object, Boolean> exec(Void... voids) {
|
public void exec(Void... voids) {
|
||||||
com.topjohnwu.magisk.components.Activity.runWithPermission(
|
getActivity().runWithPermission(
|
||||||
getActivity(), new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE },
|
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, super::exec);
|
||||||
() -> super.exec(voids));
|
|
||||||
return this;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ProgressInputStream extends FilterInputStream {
|
private class ProgressInputStream extends FilterInputStream {
|
||||||
@@ -170,14 +167,15 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|||||||
|
|
||||||
private void updateDlProgress(int step) {
|
private void updateDlProgress(int step) {
|
||||||
progress += step;
|
progress += step;
|
||||||
progressDialog.setMessage(getActivity().getString(R.string.zip_download_msg, (int) (100 * (double) progress / total + 0.5)));
|
progressDialog.setMessage(getActivity().getString(R.string.zip_download_msg,
|
||||||
|
(int) (100 * (double) progress / total + 0.5)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized int read() throws IOException {
|
public synchronized int read() throws IOException {
|
||||||
int b = super.read();
|
int b = super.read();
|
||||||
if (b > 0) {
|
if (b > 0) {
|
||||||
mHandler.post(() -> updateDlProgress(1));
|
Data.mainHandler.post(() -> updateDlProgress(1));
|
||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
@@ -191,7 +189,7 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
|
|||||||
public synchronized int read(@NonNull byte[] b, int off, int len) throws IOException {
|
public synchronized int read(@NonNull byte[] b, int off, int len) throws IOException {
|
||||||
int read = super.read(b, off, len);
|
int read = super.read(b, off, len);
|
||||||
if (read > 0) {
|
if (read > 0) {
|
||||||
mHandler.post(() -> updateDlProgress(read));
|
Data.mainHandler.post(() -> updateDlProgress(read));
|
||||||
}
|
}
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.asyncs;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.ProgressDialog;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
|
||||||
|
|
||||||
public class RestoreImages extends ParallelTask<Void, Void, Boolean> {
|
|
||||||
|
|
||||||
private ProgressDialog dialog;
|
|
||||||
|
|
||||||
public RestoreImages(Activity activity) {
|
|
||||||
super(activity);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPreExecute() {
|
|
||||||
Activity a = getActivity();
|
|
||||||
dialog = ProgressDialog.show(a, a.getString(R.string.restore_img), a.getString(R.string.restore_img_msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Boolean doInBackground(Void... voids) {
|
|
||||||
return ShellUtils.fastCmdResult("restore_imgs");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostExecute(Boolean result) {
|
|
||||||
dialog.cancel();
|
|
||||||
if (result) {
|
|
||||||
MagiskManager.toast(R.string.restore_done, Toast.LENGTH_SHORT);
|
|
||||||
} else {
|
|
||||||
MagiskManager.toast(R.string.restore_fail, Toast.LENGTH_LONG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,13 +2,13 @@ package com.topjohnwu.magisk.asyncs;
|
|||||||
|
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.text.TextUtils;
|
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.ReposFragment;
|
|
||||||
import com.topjohnwu.magisk.container.Repo;
|
import com.topjohnwu.magisk.container.Repo;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
import com.topjohnwu.magisk.utils.Logger;
|
import com.topjohnwu.magisk.utils.Logger;
|
||||||
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
import com.topjohnwu.magisk.utils.WebService;
|
import com.topjohnwu.magisk.utils.WebService;
|
||||||
|
|
||||||
@@ -20,64 +20,41 @@ import java.net.HttpURLConnection;
|
|||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.ParseException;
|
import java.text.ParseException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.TimeZone;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class UpdateRepos extends ParallelTask<Void, Void, Void> {
|
public class UpdateRepos {
|
||||||
|
|
||||||
private static final int CHECK_ETAG = 0;
|
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
|
||||||
private static final int LOAD_NEXT = 1;
|
private static final int CORE_POOL_SIZE = Math.max(2, CPU_COUNT - 1);
|
||||||
private static final int LOAD_PREV = 2;
|
private static final DateFormat dateFormat;
|
||||||
private static final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
|
||||||
|
|
||||||
private MagiskManager mm;
|
static {
|
||||||
private List<String> etags, newEtags = new LinkedList<>();
|
dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||||
private Set<String> cached;
|
dateFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||||
private boolean forceUpdate;
|
|
||||||
private AtomicInteger taskCount = new AtomicInteger(0);
|
|
||||||
final private Object allDone = new Object();
|
|
||||||
|
|
||||||
public UpdateRepos(boolean force) {
|
|
||||||
mm = MagiskManager.get();
|
|
||||||
mm.repoLoadDone.reset();
|
|
||||||
forceUpdate = force;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void queueTask(Runnable task) {
|
private MagiskManager mm;
|
||||||
// Thread pool's queue has an upper bound, batch it with 64 tasks
|
private Set<String> cached;
|
||||||
while (taskCount.get() >= 64) {
|
private ExecutorService threadPool;
|
||||||
waitTasks();
|
|
||||||
}
|
public UpdateRepos() {
|
||||||
taskCount.incrementAndGet();
|
mm = Data.MM();
|
||||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
|
||||||
task.run();
|
|
||||||
if (taskCount.decrementAndGet() == 0) {
|
|
||||||
synchronized (allDone) {
|
|
||||||
allDone.notify();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void waitTasks() {
|
private void waitTasks() {
|
||||||
if (taskCount.get() == 0)
|
threadPool.shutdown();
|
||||||
return;
|
try {
|
||||||
synchronized (allDone) {
|
threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
|
||||||
try {
|
} catch (InterruptedException ignored) {}
|
||||||
allDone.wait();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
// Wait again
|
|
||||||
waitTasks();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean loadJSON(String jsonString) throws JSONException, ParseException {
|
private boolean loadJSON(String jsonString) throws JSONException, ParseException {
|
||||||
@@ -89,20 +66,17 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
|
|||||||
|
|
||||||
for (int i = 0; i < jsonArray.length(); i++) {
|
for (int i = 0; i < jsonArray.length(); i++) {
|
||||||
JSONObject rawRepo = jsonArray.getJSONObject(i);
|
JSONObject rawRepo = jsonArray.getJSONObject(i);
|
||||||
String id = rawRepo.getString("description");
|
String id = rawRepo.getString("name");
|
||||||
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(() -> {
|
||||||
queueTask(() -> {
|
|
||||||
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);
|
||||||
publishProgress();
|
|
||||||
} catch (Repo.IllegalRepoException e) {
|
} catch (Repo.IllegalRepoException e) {
|
||||||
Logger.debug(e.getMessage());
|
Logger.debug(e.getMessage());
|
||||||
mm.repoDB.removeRepo(id);
|
mm.repoDB.removeRepo(id);
|
||||||
@@ -112,99 +86,74 @@ public class UpdateRepos extends ParallelTask<Void, Void, Void> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean loadPage(int page, int mode) {
|
/* 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
|
||||||
|
*/
|
||||||
|
private boolean loadPage(int page) {
|
||||||
Map<String, String> header = new HashMap<>();
|
Map<String, String> header = new HashMap<>();
|
||||||
if (mode == CHECK_ETAG && page < etags.size())
|
if (page == 0)
|
||||||
header.put(Const.Key.IF_NONE_MATCH, etags.get(page));
|
header.put(Const.Key.IF_NONE_MATCH, mm.prefs.getString(Const.Key.ETAG_KEY, ""));
|
||||||
String url = Utils.fmt(Const.Url.REPO_URL, page + 1);
|
String url = Utils.fmt(Const.Url.REPO_URL, page + 1);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
HttpURLConnection conn = WebService.request(url, header);
|
HttpURLConnection conn = WebService.request(url, header);
|
||||||
if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED) {
|
// No updates
|
||||||
// Current page is not updated, check the next page
|
if (conn.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED)
|
||||||
return loadPage(page + 1, CHECK_ETAG);
|
return false;
|
||||||
}
|
// Current page is the last page
|
||||||
if (!loadJSON(WebService.getString(conn)))
|
if (!loadJSON(WebService.getString(conn)))
|
||||||
return mode != CHECK_ETAG;
|
return true;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
// Should not happen, but if exception occurs, page load fails
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If one page is updated, we force update all pages */
|
|
||||||
|
|
||||||
// Update ETAG
|
// Update ETAG
|
||||||
String etag = header.get(Const.Key.ETAG_KEY);
|
if (page == 0) {
|
||||||
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
String etag = header.get(Const.Key.ETAG_KEY);
|
||||||
if (mode == LOAD_PREV) {
|
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
||||||
// We are loading a previous page, push the new tag to the front
|
mm.prefs.edit().putString(Const.Key.ETAG_KEY, etag).apply();
|
||||||
newEtags.add(0, etag);
|
|
||||||
} else {
|
|
||||||
newEtags.add(etag);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String links = header.get(Const.Key.LINK_KEY);
|
String links = header.get(Const.Key.LINK_KEY);
|
||||||
if (links != null) {
|
return links == null || !links.contains("next") || loadPage(page + 1);
|
||||||
for (String s : links.split(", ")) {
|
}
|
||||||
if (mode != LOAD_PREV && s.contains("next")) {
|
|
||||||
// Force load all next pages
|
private void fullReload() {
|
||||||
loadPage(page + 1, LOAD_NEXT);
|
Cursor c = mm.repoDB.getRawCursor();
|
||||||
|
while (c.moveToNext()) {
|
||||||
|
Repo repo = new Repo(c);
|
||||||
|
threadPool.execute(() -> {
|
||||||
|
try {
|
||||||
|
repo.update();
|
||||||
|
mm.repoDB.addRepo(repo);
|
||||||
|
} catch (Repo.IllegalRepoException e) {
|
||||||
|
Logger.debug(e.getMessage());
|
||||||
|
mm.repoDB.removeRepo(repo);
|
||||||
}
|
}
|
||||||
if (mode != LOAD_NEXT && s.contains("prev")) {
|
});
|
||||||
// Back propagation
|
|
||||||
loadPage(page - 1, LOAD_PREV);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
waitTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void exec(boolean force) {
|
||||||
protected void onProgressUpdate(Void... values) {
|
Topic.reset(Topic.REPO_LOAD_DONE);
|
||||||
if (ReposFragment.adapter != null)
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
ReposFragment.adapter.notifyDBChanged();
|
cached = Collections.synchronizedSet(mm.repoDB.getRepoIDSet());
|
||||||
}
|
threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE);
|
||||||
|
|
||||||
@Override
|
if (loadPage(0)) {
|
||||||
protected void onPreExecute() {
|
waitTasks();
|
||||||
mm.repoLoadDone.setPending();
|
// The leftover cached means they are removed from online repo
|
||||||
}
|
mm.repoDB.removeRepo(cached);
|
||||||
|
} else if (force) {
|
||||||
@Override
|
fullReload();
|
||||||
protected Void doInBackground(Void... voids) {
|
|
||||||
etags = Arrays.asList(mm.prefs.getString(Const.Key.ETAG_KEY, "").split(","));
|
|
||||||
cached = mm.repoDB.getRepoIDSet();
|
|
||||||
|
|
||||||
if (loadPage(0, CHECK_ETAG)) {
|
|
||||||
waitTasks();
|
|
||||||
|
|
||||||
// The leftover cached means they are removed from online repo
|
|
||||||
mm.repoDB.removeRepo(cached);
|
|
||||||
|
|
||||||
// Update ETag
|
|
||||||
mm.prefs.edit().putString(Const.Key.ETAG_KEY, TextUtils.join(",", newEtags)).apply();
|
|
||||||
} else if (forceUpdate) {
|
|
||||||
Cursor c = mm.repoDB.getRawCursor();
|
|
||||||
while (c.moveToNext()) {
|
|
||||||
Repo repo = new Repo(c);
|
|
||||||
queueTask(() -> {
|
|
||||||
try {
|
|
||||||
repo.update();
|
|
||||||
mm.repoDB.addRepo(repo);
|
|
||||||
} catch (Repo.IllegalRepoException e) {
|
|
||||||
Logger.debug(e.getMessage());
|
|
||||||
mm.repoDB.removeRepo(repo);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
waitTasks();
|
Topic.publish(Topic.REPO_LOAD_DONE);
|
||||||
}
|
});
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void exec() {
|
||||||
protected void onPostExecute(Void v) {
|
exec(false);
|
||||||
mm.repoLoadDone.publish();
|
|
||||||
super.onPostExecute(v);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,19 +28,17 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
|
|
||||||
|
import butterknife.BindView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author dvdandroid
|
* @author dvdandroid
|
||||||
*/
|
*/
|
||||||
public class AboutCardRow extends LinearLayout {
|
public class AboutCardRow extends LinearLayout {
|
||||||
|
|
||||||
private final String title;
|
@BindView(android.R.id.title) TextView mTitle;
|
||||||
private final Drawable icon;
|
@BindView(android.R.id.summary) TextView mSummary;
|
||||||
|
@BindView(android.R.id.icon) ImageView mIcon;
|
||||||
private final TextView mTitle;
|
@BindView(R.id.container) View mView;
|
||||||
private final TextView mSummary;
|
|
||||||
private final ImageView mIcon;
|
|
||||||
|
|
||||||
private final View mView;
|
|
||||||
|
|
||||||
public AboutCardRow(Context context) {
|
public AboutCardRow(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
@@ -53,37 +51,28 @@ 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);
|
||||||
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AboutCardRow, 0, 0);
|
new AboutCardRow_ViewBinding(this, this);
|
||||||
|
|
||||||
|
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AboutCardRow, 0, 0);
|
||||||
|
String title;
|
||||||
|
Drawable icon;
|
||||||
try {
|
try {
|
||||||
title = a.getString(R.styleable.AboutCardRow_text);
|
title = a.getString(R.styleable.AboutCardRow_text);
|
||||||
icon = a.getDrawable(R.styleable.AboutCardRow_icon);
|
icon = a.getDrawable(R.styleable.AboutCardRow_icon);
|
||||||
} finally {
|
} finally {
|
||||||
a.recycle();
|
a.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
mView = findViewById(R.id.container);
|
|
||||||
|
|
||||||
mTitle = (TextView) findViewById(android.R.id.title);
|
|
||||||
mSummary = (TextView) findViewById(android.R.id.summary);
|
|
||||||
mIcon = (ImageView) findViewById(android.R.id.icon);
|
|
||||||
|
|
||||||
mTitle.setText(title);
|
mTitle.setText(title);
|
||||||
mIcon.setImageDrawable(icon);
|
mIcon.setImageDrawable(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOnClickListener(OnClickListener l) {
|
public void setOnClickListener(OnClickListener l) {
|
||||||
super.setOnClickListener(l);
|
|
||||||
|
|
||||||
mView.setOnClickListener(l);
|
mView.setOnClickListener(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSummary(String s) {
|
public void setSummary(String s) {
|
||||||
|
mSummary.setVisibility(VISIBLE);
|
||||||
mSummary.setText(s);
|
mSummary.setText(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeSummary() {
|
|
||||||
mSummary.setVisibility(GONE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.components;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
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.View;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
|
|
||||||
import butterknife.BindView;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class AlertDialogBuilder extends AlertDialog.Builder {
|
|
||||||
|
|
||||||
@BindView(R.id.button_panel) LinearLayout buttons;
|
|
||||||
@BindView(R.id.message_panel) LinearLayout messagePanel;
|
|
||||||
|
|
||||||
@BindView(R.id.negative) Button negative;
|
|
||||||
@BindView(R.id.positive) Button positive;
|
|
||||||
@BindView(R.id.neutral) Button neutral;
|
|
||||||
@BindView(R.id.message) TextView messageView;
|
|
||||||
|
|
||||||
private DialogInterface.OnClickListener positiveListener;
|
|
||||||
private DialogInterface.OnClickListener negativeListener;
|
|
||||||
private DialogInterface.OnClickListener neutralListener;
|
|
||||||
|
|
||||||
private AlertDialog dialog;
|
|
||||||
|
|
||||||
public AlertDialogBuilder(@NonNull Activity context) {
|
|
||||||
super(context);
|
|
||||||
setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AlertDialogBuilder(@NonNull Activity context, @StyleRes int themeResId) {
|
|
||||||
super(context, themeResId);
|
|
||||||
setup();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setup() {
|
|
||||||
View v = LayoutInflater.from(getContext()).inflate(R.layout.alert_dialog, null);
|
|
||||||
ButterKnife.bind(this, v);
|
|
||||||
super.setView(v);
|
|
||||||
negative.setVisibility(View.GONE);
|
|
||||||
positive.setVisibility(View.GONE);
|
|
||||||
neutral.setVisibility(View.GONE);
|
|
||||||
buttons.setVisibility(View.GONE);
|
|
||||||
messagePanel.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog.Builder setTitle(int titleId) {
|
|
||||||
return super.setTitle(titleId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog.Builder setView(int layoutResId) { return this; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog.Builder setView(View view) { return this; }
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog.Builder setMessage(@Nullable CharSequence message) {
|
|
||||||
messageView.setText(message);
|
|
||||||
messagePanel.setVisibility(View.VISIBLE);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog.Builder setMessage(@StringRes int messageId) {
|
|
||||||
return setMessage(getContext().getString(messageId));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog.Builder setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener) {
|
|
||||||
buttons.setVisibility(View.VISIBLE);
|
|
||||||
positive.setVisibility(View.VISIBLE);
|
|
||||||
positive.setText(text);
|
|
||||||
positiveListener = listener;
|
|
||||||
positive.setOnClickListener((v) -> {
|
|
||||||
if (positiveListener != null) {
|
|
||||||
positiveListener.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
|
|
||||||
}
|
|
||||||
dialog.dismiss();
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog.Builder setPositiveButton(@StringRes int textId, DialogInterface.OnClickListener listener) {
|
|
||||||
return setPositiveButton(getContext().getString(textId), listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog.Builder setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener) {
|
|
||||||
buttons.setVisibility(View.VISIBLE);
|
|
||||||
negative.setVisibility(View.VISIBLE);
|
|
||||||
negative.setText(text);
|
|
||||||
negativeListener = listener;
|
|
||||||
negative.setOnClickListener((v) -> {
|
|
||||||
if (negativeListener != null) {
|
|
||||||
negativeListener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
|
|
||||||
}
|
|
||||||
dialog.dismiss();
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog.Builder setNegativeButton(@StringRes int textId, DialogInterface.OnClickListener listener) {
|
|
||||||
return setNegativeButton(getContext().getString(textId), listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog.Builder setNeutralButton(CharSequence text, DialogInterface.OnClickListener listener) {
|
|
||||||
buttons.setVisibility(View.VISIBLE);
|
|
||||||
neutral.setVisibility(View.VISIBLE);
|
|
||||||
neutral.setText(text);
|
|
||||||
neutralListener = listener;
|
|
||||||
neutral.setOnClickListener((v) -> {
|
|
||||||
if (neutralListener != null) {
|
|
||||||
neutralListener.onClick(dialog, DialogInterface.BUTTON_NEUTRAL);
|
|
||||||
}
|
|
||||||
dialog.dismiss();
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog.Builder setNeutralButton(@StringRes int textId, DialogInterface.OnClickListener listener) {
|
|
||||||
return setNeutralButton(getContext().getString(textId), listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog create() {
|
|
||||||
dialog = super.create();
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AlertDialog show() {
|
|
||||||
create();
|
|
||||||
dialog.show();
|
|
||||||
return dialog;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment;
|
||||||
|
import butterknife.Unbinder;
|
||||||
|
|
||||||
|
public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
|
||||||
|
|
||||||
|
public MagiskManager mm;
|
||||||
|
protected Unbinder unbinder = null;
|
||||||
|
|
||||||
|
public BaseFragment() {
|
||||||
|
mm = Data.MM();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
Topic.subscribe(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
Topic.unsubscribe(this);
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
super.onDestroyView();
|
||||||
|
if (unbinder != null)
|
||||||
|
unbinder.unbind();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void startActivityForResult(Intent intent, int requestCode) {
|
||||||
|
startActivityForResult(intent, requestCode, this::onActivityResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startActivityForResult(Intent intent, int requestCode, BaseActivity.ActivityResultListener listener) {
|
||||||
|
((BaseActivity) requireActivity()).startActivityForResult(intent, requestCode, listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void runWithPermission(String[] permissions, Runnable callback) {
|
||||||
|
((BaseActivity) requireActivity()).runWithPermission(permissions,callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getSubscribedTopics() {
|
||||||
|
return FlavorActivity.EMPTY_INT_ARRAY;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public class CustomAlertDialog extends AlertDialog.Builder {
|
||||||
|
|
||||||
|
private DialogInterface.OnClickListener positiveListener;
|
||||||
|
private DialogInterface.OnClickListener negativeListener;
|
||||||
|
private DialogInterface.OnClickListener neutralListener;
|
||||||
|
private AlertDialog dialog;
|
||||||
|
|
||||||
|
private ViewHolder vh;
|
||||||
|
|
||||||
|
public class ViewHolder {
|
||||||
|
@BindView(R.id.dialog_layout) public LinearLayout dialogLayout;
|
||||||
|
@BindView(R.id.button_panel) public LinearLayout buttons;
|
||||||
|
|
||||||
|
@BindView(R.id.message) public TextView messageView;
|
||||||
|
@BindView(R.id.negative) public Button negative;
|
||||||
|
@BindView(R.id.positive) public Button positive;
|
||||||
|
@BindView(R.id.neutral) public Button neutral;
|
||||||
|
|
||||||
|
ViewHolder(View v) {
|
||||||
|
new CustomAlertDialog$ViewHolder_ViewBinding(this, v);
|
||||||
|
messageView.setVisibility(View.GONE);
|
||||||
|
negative.setVisibility(View.GONE);
|
||||||
|
positive.setVisibility(View.GONE);
|
||||||
|
neutral.setVisibility(View.GONE);
|
||||||
|
buttons.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
View v = LayoutInflater.from(getContext()).inflate(R.layout.alert_dialog, null);
|
||||||
|
vh = new ViewHolder(v);
|
||||||
|
super.setView(v);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomAlertDialog(@NonNull Activity context) {
|
||||||
|
super(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public CustomAlertDialog(@NonNull Activity context, @StyleRes int themeResId) {
|
||||||
|
super(context, themeResId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ViewHolder getViewHolder() {
|
||||||
|
return vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomAlertDialog setView(int layoutResId) { return this; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomAlertDialog setView(View view) { return this; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomAlertDialog setMessage(@Nullable CharSequence message) {
|
||||||
|
vh.messageView.setVisibility(View.VISIBLE);
|
||||||
|
vh.messageView.setText(message);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomAlertDialog setMessage(@StringRes int messageId) {
|
||||||
|
return setMessage(getContext().getString(messageId));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomAlertDialog setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener) {
|
||||||
|
vh.buttons.setVisibility(View.VISIBLE);
|
||||||
|
vh.positive.setVisibility(View.VISIBLE);
|
||||||
|
vh.positive.setText(text);
|
||||||
|
positiveListener = listener;
|
||||||
|
vh.positive.setOnClickListener((v) -> {
|
||||||
|
if (positiveListener != null) {
|
||||||
|
positiveListener.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
|
||||||
|
}
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomAlertDialog setPositiveButton(@StringRes int textId, DialogInterface.OnClickListener listener) {
|
||||||
|
return setPositiveButton(getContext().getString(textId), listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomAlertDialog setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener) {
|
||||||
|
vh.buttons.setVisibility(View.VISIBLE);
|
||||||
|
vh.negative.setVisibility(View.VISIBLE);
|
||||||
|
vh.negative.setText(text);
|
||||||
|
negativeListener = listener;
|
||||||
|
vh.negative.setOnClickListener((v) -> {
|
||||||
|
if (negativeListener != null) {
|
||||||
|
negativeListener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
|
||||||
|
}
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomAlertDialog setNegativeButton(@StringRes int textId, DialogInterface.OnClickListener listener) {
|
||||||
|
return setNegativeButton(getContext().getString(textId), listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomAlertDialog setNeutralButton(CharSequence text, DialogInterface.OnClickListener listener) {
|
||||||
|
vh.buttons.setVisibility(View.VISIBLE);
|
||||||
|
vh.neutral.setVisibility(View.VISIBLE);
|
||||||
|
vh.neutral.setText(text);
|
||||||
|
neutralListener = listener;
|
||||||
|
vh.neutral.setOnClickListener((v) -> {
|
||||||
|
if (neutralListener != null) {
|
||||||
|
neutralListener.onClick(dialog, DialogInterface.BUTTON_NEUTRAL);
|
||||||
|
}
|
||||||
|
dialog.dismiss();
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CustomAlertDialog setNeutralButton(@StringRes int textId, DialogInterface.OnClickListener listener) {
|
||||||
|
return setNeutralButton(getContext().getString(textId), listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AlertDialog create() {
|
||||||
|
dialog = super.create();
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AlertDialog show() {
|
||||||
|
create();
|
||||||
|
dialog.show();
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void dismiss() {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.asyncs.InstallMagisk;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class EnvFixDialog extends CustomAlertDialog {
|
||||||
|
|
||||||
|
public EnvFixDialog(@NonNull Activity activity) {
|
||||||
|
super(activity);
|
||||||
|
setTitle(R.string.env_fix_title);
|
||||||
|
setMessage(R.string.env_fix_msg);
|
||||||
|
setCancelable(true);
|
||||||
|
setPositiveButton(R.string.yes, (d, i) -> new InstallMagisk(activity).exec());
|
||||||
|
setNegativeButton(R.string.no_thanks, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,50 +1,58 @@
|
|||||||
package com.topjohnwu.magisk.components;
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
import android.content.res.AssetManager;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Keep;
|
|
||||||
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 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.LocaleManager;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
|
||||||
public abstract class FlavorActivity extends AppCompatActivity {
|
import androidx.annotation.Nullable;
|
||||||
|
import androidx.annotation.StyleRes;
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
private AssetManager swappedAssetManager = null;
|
public abstract class FlavorActivity extends AppCompatActivity implements Topic.AutoSubscriber {
|
||||||
private Resources swappedResources = null;
|
|
||||||
private Resources.Theme backupTheme = null;
|
private ActivityResultListener activityResultListener;
|
||||||
|
static int[] EMPTY_INT_ARRAY = new int[0];
|
||||||
|
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
|
||||||
|
public int[] getSubscribedTopics() {
|
||||||
|
return EMPTY_INT_ARRAY;
|
||||||
|
}
|
||||||
|
|
||||||
@StyleRes
|
@StyleRes
|
||||||
public int getDarkTheme() {
|
public int getDarkTheme() {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public MagiskManager getMagiskManager() {
|
|
||||||
return (MagiskManager) super.getApplication();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
Topic.subscribe(this);
|
||||||
if (this instanceof Topic.Subscriber) {
|
if (Data.isDarkTheme && getDarkTheme() != -1) {
|
||||||
((Topic.Subscriber) this).subscribeTopics();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getMagiskManager().isDarkTheme && getDarkTheme() != -1) {
|
|
||||||
setTheme(getDarkTheme());
|
setTheme(getDarkTheme());
|
||||||
}
|
}
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDestroy() {
|
protected void onDestroy() {
|
||||||
if (this instanceof Topic.Subscriber) {
|
Topic.unsubscribe(this);
|
||||||
((Topic.Subscriber) this).unsubscribeTopics();
|
|
||||||
}
|
|
||||||
super.onDestroy();
|
super.onDestroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,46 +71,18 @@ public abstract class FlavorActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resources.Theme getTheme() {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
return backupTheme == null ? super.getTheme() : backupTheme;
|
if (activityResultListener != null)
|
||||||
|
activityResultListener.onActivityResult(requestCode, resultCode, data);
|
||||||
|
activityResultListener = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void startActivityForResult(Intent intent, int requestCode, ActivityResultListener listener) {
|
||||||
public AssetManager getAssets() {
|
activityResultListener = listener;
|
||||||
return swappedAssetManager == null ? super.getAssets() : swappedAssetManager;
|
super.startActivityForResult(intent, requestCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AssetManager getAssets(String apk) {
|
public interface ActivityResultListener {
|
||||||
try {
|
void onActivityResult(int requestCode, int resultCode, Intent data);
|
||||||
AssetManager asset = AssetManager.class.newInstance();
|
|
||||||
AssetManager.class.getMethod("addAssetPath", String.class).invoke(asset, apk);
|
|
||||||
return asset;
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Resources getResources() {
|
|
||||||
return swappedResources == null ? super.getResources() : swappedResources;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Keep
|
|
||||||
public void swapResources(String dexPath) {
|
|
||||||
AssetManager asset = getAssets(dexPath);
|
|
||||||
if (asset != null) {
|
|
||||||
backupTheme = super.getTheme();
|
|
||||||
Resources res = super.getResources();
|
|
||||||
swappedResources = new Resources(asset, res.getDisplayMetrics(), res.getConfiguration());
|
|
||||||
swappedAssetManager = asset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Keep
|
|
||||||
public void restoreResources() {
|
|
||||||
swappedAssetManager = null;
|
|
||||||
swappedResources = null;
|
|
||||||
backupTheme = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.components;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
|
|
||||||
public class Fragment extends android.support.v4.app.Fragment {
|
|
||||||
|
|
||||||
public MagiskManager getApplication() {
|
|
||||||
return Utils.getMagiskManager(getActivity());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume() {
|
|
||||||
super.onResume();
|
|
||||||
if (this instanceof Topic.Subscriber) {
|
|
||||||
((Topic.Subscriber) this).subscribeTopics();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause() {
|
|
||||||
if (this instanceof Topic.Subscriber) {
|
|
||||||
((Topic.Subscriber) this).unsubscribeTopics();
|
|
||||||
}
|
|
||||||
super.onPause();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startActivityForResult(Intent intent, int requestCode) {
|
|
||||||
startActivityForResult(intent, requestCode, this::onActivityResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void startActivityForResult(Intent intent, int requestCode, Activity.ActivityResultListener listener) {
|
|
||||||
((Activity) getActivity()).startActivityForResult(intent, requestCode, listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void runWithPermission(String[] permissions, Runnable callback) {
|
|
||||||
Activity.runWithPermission(getActivity(), permissions, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
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.receivers.DownloadReceiver;
|
||||||
|
import com.topjohnwu.magisk.utils.Download;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AlertDialog;
|
||||||
|
|
||||||
|
class InstallMethodDialog extends AlertDialog.Builder {
|
||||||
|
|
||||||
|
InstallMethodDialog(BaseActivity activity, List<String> options) {
|
||||||
|
super(activity);
|
||||||
|
setTitle(R.string.select_method);
|
||||||
|
setItems(options.toArray(new String [0]), (dialog, idx) -> {
|
||||||
|
Intent intent;
|
||||||
|
switch (idx) {
|
||||||
|
case 1:
|
||||||
|
Utils.toast(R.string.boot_file_patch_msg, Toast.LENGTH_LONG);
|
||||||
|
intent = new Intent(Intent.ACTION_GET_CONTENT).setType("*/*");
|
||||||
|
activity.runWithPermission(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, () ->
|
||||||
|
activity.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(activity, Data.classMap.get(FlashActivity.class))
|
||||||
|
.putExtra(Const.Key.FLASH_SET_BOOT, data.getData())
|
||||||
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_BOOT);
|
||||||
|
activity.startActivity(i);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 0:
|
||||||
|
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
||||||
|
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;
|
||||||
|
case 2:
|
||||||
|
intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
|
||||||
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
|
||||||
|
activity.startActivity(intent);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
new CustomAlertDialog(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, Data.classMap.get(FlashActivity.class))
|
||||||
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_INACTIVE_SLOT);
|
||||||
|
activity.startActivity(it);
|
||||||
|
})
|
||||||
|
.setNegativeButton(R.string.no_thanks, null)
|
||||||
|
.show();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MagiskInstallDialog extends CustomAlertDialog {
|
||||||
|
public MagiskInstallDialog(BaseActivity activity) {
|
||||||
|
super(activity);
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
||||||
|
Data.remoteMagiskVersionString, Data.remoteMagiskVersionCode);
|
||||||
|
setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.magisk)));
|
||||||
|
setMessage(mm.getString(R.string.repo_install_msg, filename));
|
||||||
|
setCancelable(true);
|
||||||
|
setPositiveButton(R.string.install, (d, i) -> {
|
||||||
|
List<String> options = new ArrayList<>();
|
||||||
|
options.add(mm.getString(R.string.download_zip_only));
|
||||||
|
options.add(mm.getString(R.string.patch_boot_file));
|
||||||
|
if (Shell.rootAccess()) {
|
||||||
|
options.add(mm.getString(R.string.direct_install));
|
||||||
|
String s = ShellUtils.fastCmd("grep_prop ro.build.ab_update");
|
||||||
|
if (!s.isEmpty() && Boolean.parseBoolean(s)) {
|
||||||
|
options.add(mm.getString(R.string.install_inactive_slot));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
new InstallMethodDialog(activity, options).show();
|
||||||
|
});
|
||||||
|
setNegativeButton(R.string.no_thanks, null);
|
||||||
|
if (!TextUtils.isEmpty(Data.magiskNoteLink)) {
|
||||||
|
setNeutralButton(R.string.release_notes, (d, i) -> {
|
||||||
|
if (Data.magiskNoteLink.contains("forum.xda-developers")) {
|
||||||
|
// Open forum links in browser
|
||||||
|
Utils.openLink(activity, Uri.parse(Data.magiskNoteLink));
|
||||||
|
} else {
|
||||||
|
new MarkDownWindow(activity, null, Data.magiskNoteLink).exec();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
||||||
|
import com.topjohnwu.magisk.receivers.ManagerUpdate;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class ManagerInstallDialog extends CustomAlertDialog {
|
||||||
|
|
||||||
|
public ManagerInstallDialog(@NonNull BaseActivity activity) {
|
||||||
|
super(activity);
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
String filename = Utils.fmt("MagiskManager-v%s(%d).apk",
|
||||||
|
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
|
||||||
|
setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.app_name)));
|
||||||
|
setMessage(mm.getString(R.string.repo_install_msg, filename));
|
||||||
|
setCancelable(true);
|
||||||
|
setPositiveButton(R.string.install, (d, i) -> activity.runWithPermission(
|
||||||
|
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
|
||||||
|
Intent intent = new Intent(mm, Data.classMap.get(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)) {
|
||||||
|
setNeutralButton(R.string.app_changelog, (d, i) ->
|
||||||
|
new MarkDownWindow(activity, null, Data.managerNoteLink).exec());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,13 +35,13 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void showUri(Activity activity, Uri uri) {
|
public static void showUri(Activity activity, Uri uri) {
|
||||||
make(activity, activity.getString(R.string.internal_storage,
|
make(activity, activity.getString(R.string.internal_storage,
|
||||||
"/MagiskManager/" + Utils.getNameFromUri(activity, uri)),
|
"/Download/" + Utils.getNameFromUri(activity, uri)),
|
||||||
Snackbar.LENGTH_LONG)
|
Snackbar.LENGTH_LONG)
|
||||||
.setAction(R.string.ok, (v)->{}).show();
|
.setAction(R.string.ok, (v)->{}).show();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
package com.topjohnwu.magisk.components;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
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.superuser.Shell;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class UninstallDialog extends CustomAlertDialog {
|
||||||
|
|
||||||
|
public UninstallDialog(@NonNull Activity activity) {
|
||||||
|
super(activity);
|
||||||
|
MagiskManager mm = Data.MM();
|
||||||
|
setTitle(R.string.uninstall_magisk_title);
|
||||||
|
setMessage(R.string.uninstall_magisk_msg);
|
||||||
|
setNeutralButton(R.string.restore_img, (d, i) -> {
|
||||||
|
ProgressDialog dialog = ProgressDialog.show(activity,
|
||||||
|
activity.getString(R.string.restore_img),
|
||||||
|
activity.getString(R.string.restore_img_msg));
|
||||||
|
Shell.su("restore_imgs").submit(result -> {
|
||||||
|
dialog.cancel();
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
Utils.toast(R.string.restore_done, Toast.LENGTH_SHORT);
|
||||||
|
} else {
|
||||||
|
Utils.toast(R.string.restore_fail, Toast.LENGTH_LONG);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (!TextUtils.isEmpty(Data.uninstallerLink)) {
|
||||||
|
setPositiveButton(R.string.complete_uninstall, (d, i) ->
|
||||||
|
Download.receive(activity, new DownloadReceiver() {
|
||||||
|
@Override
|
||||||
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
|
Intent intent = new Intent(context, Data.classMap.get(FlashActivity.class))
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||||
|
.setData(uri)
|
||||||
|
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL);
|
||||||
|
context.startActivity(intent);
|
||||||
|
}
|
||||||
|
}, Data.uninstallerLink, "magisk-uninstaller.zip"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ public class Module extends BaseModule {
|
|||||||
parseProps(Shell.Sync.su("dos2unix < " + path + "/module.prop"));
|
parseProps(Shell.Sync.su("dos2unix < " + path + "/module.prop"));
|
||||||
} 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() == null) {
|
||||||
int sep = path.lastIndexOf('/');
|
int sep = path.lastIndexOf('/');
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ 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.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,7 +28,7 @@ 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(Cursor c, PackageManager pm) throws PackageManager.NameNotFoundException {
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package com.topjohnwu.magisk.container;
|
|||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
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 com.topjohnwu.magisk.utils.WebService;
|
||||||
@@ -14,16 +14,14 @@ 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")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,17 +30,14 @@ public class Repo extends BaseModule {
|
|||||||
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,36 +49,34 @@ 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 getManifestUrl() {
|
||||||
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() {
|
||||||
return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM,
|
return DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM).format(mLastUpdate);
|
||||||
MagiskManager.locale).format(mLastUpdate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Date getLastUpdate() {
|
public Date getLastUpdate() {
|
||||||
return mLastUpdate;
|
return mLastUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDownloadFilename() {
|
||||||
|
return Download.getLegalFilename(getName() + "-" + getVersion() + ".zip");
|
||||||
|
}
|
||||||
|
|
||||||
public class IllegalRepoException extends Exception {
|
public class IllegalRepoException extends Exception {
|
||||||
IllegalRepoException(String message) {
|
IllegalRepoException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package com.topjohnwu.magisk.container;
|
|||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||||
|
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
@@ -47,10 +47,10 @@ public class SuLogEntry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String getDateString() {
|
public String getDateString() {
|
||||||
return DateFormat.getDateInstance(DateFormat.MEDIUM, MagiskManager.locale).format(date);
|
return DateFormat.getDateInstance(DateFormat.MEDIUM, LocaleManager.locale).format(date);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTimeString() {
|
public String getTimeString() {
|
||||||
return new SimpleDateFormat("h:mm a", MagiskManager.locale).format(date);
|
return new SimpleDateFormat("h:mm a", LocaleManager.locale).format(date);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<>();
|
||||||
|
|||||||
@@ -7,15 +7,16 @@ import android.database.Cursor;
|
|||||||
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;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
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.container.Policy;
|
import com.topjohnwu.magisk.container.Policy;
|
||||||
import com.topjohnwu.magisk.container.SuLogEntry;
|
import com.topjohnwu.magisk.container.SuLogEntry;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||||
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.io.SuFile;
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
@@ -27,85 +28,84 @@ import java.util.Collections;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
public class MagiskDatabaseHelper {
|
public class MagiskDatabaseHelper {
|
||||||
|
|
||||||
private static final int DATABASE_VER = 5;
|
private static final int DATABASE_VER = 6;
|
||||||
|
private static final int OLD_DATABASE_VER = 5;
|
||||||
private static final String POLICY_TABLE = "policies";
|
private static final String POLICY_TABLE = "policies";
|
||||||
private static final String LOG_TABLE = "logs";
|
private static final String LOG_TABLE = "logs";
|
||||||
private static final String SETTINGS_TABLE = "settings";
|
private static final String SETTINGS_TABLE = "settings";
|
||||||
private static final String STRINGS_TABLE = "strings";
|
private static final String STRINGS_TABLE = "strings";
|
||||||
|
private static final File MANAGER_DB =
|
||||||
|
new File(Utils.fmt("/sbin/.core/db-%d/magisk.db", Const.USER_ID));
|
||||||
|
|
||||||
private PackageManager pm;
|
private PackageManager pm;
|
||||||
private SQLiteDatabase db;
|
private SQLiteDatabase db;
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static MagiskDatabaseHelper getInstance(MagiskManager mm) {
|
public static MagiskDatabaseHelper getInstance() {
|
||||||
try {
|
try {
|
||||||
return new MagiskDatabaseHelper(mm);
|
return new MagiskDatabaseHelper();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// Let's cleanup everything and try again
|
// Let's cleanup everything and try again
|
||||||
Shell.Sync.su("db_clean '*'");
|
Shell.su("db_clean '*'").exec();
|
||||||
return new MagiskDatabaseHelper(mm);
|
return new MagiskDatabaseHelper();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private MagiskDatabaseHelper(MagiskManager mm) {
|
private MagiskDatabaseHelper() {
|
||||||
pm = mm.getPackageManager();
|
pm = Data.MM().getPackageManager();
|
||||||
db = openDatabase(mm);
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
db = openDatabase();
|
||||||
db.disableWriteAheadLogging();
|
db.disableWriteAheadLogging();
|
||||||
int version = db.getVersion();
|
int version = Data.magiskVersionCode >= Const.MAGISK_VER.DBVER_SIX ? DATABASE_VER : OLD_DATABASE_VER;
|
||||||
if (version < DATABASE_VER) {
|
int curVersion = db.getVersion();
|
||||||
onUpgrade(db, version);
|
if (curVersion < version) {
|
||||||
} else if (version > DATABASE_VER) {
|
onUpgrade(db, curVersion);
|
||||||
|
} else if (curVersion > DATABASE_VER) {
|
||||||
|
/* Higher than we can possibly support */
|
||||||
onDowngrade(db);
|
onDowngrade(db);
|
||||||
}
|
}
|
||||||
db.setVersion(DATABASE_VER);
|
db.setVersion(version);
|
||||||
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 (!MANAGER_DB.canWrite()) {
|
||||||
if (!Shell.rootAccess()) {
|
if (!Shell.rootAccess()) {
|
||||||
// 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);
|
||||||
}
|
}
|
||||||
mm.loadMagiskInfo();
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
Shell.Sync.su("db_clean " + Const.USER_ID);
|
Shell.su("db_clean " + Const.USER_ID).exec();
|
||||||
if (mm.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 (mm.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);
|
|
||||||
} else {
|
|
||||||
// Global database
|
|
||||||
final SuFile GLOBAL_DB = new SuFile("/data/adb/magisk.db");
|
|
||||||
mm.deleteDatabase("su.db");
|
|
||||||
de.deleteDatabase("su.db");
|
|
||||||
if (mm.magiskVersionCode < Const.MAGISK_VER.SEPOL_REFACTOR) {
|
|
||||||
// We need some additional policies on old versions
|
|
||||||
Shell.Sync.su("db_sepatch");
|
|
||||||
}
|
|
||||||
if (!GLOBAL_DB.exists()) {
|
|
||||||
Shell.Sync.su("db_init");
|
|
||||||
SQLiteDatabase.openOrCreateDatabase(GLOBAL_DB, null).close();
|
|
||||||
Shell.Sync.su("db_restore");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Shell.Sync.su("db_setup " + Process.myUid());
|
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();
|
||||||
}
|
}
|
||||||
// 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(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;
|
||||||
@@ -123,7 +123,7 @@ public class MagiskDatabaseHelper {
|
|||||||
POLICY_TABLE, POLICY_TABLE));
|
POLICY_TABLE, POLICY_TABLE));
|
||||||
db.execSQL(Utils.fmt("DROP TABLE %s_old", POLICY_TABLE));
|
db.execSQL(Utils.fmt("DROP TABLE %s_old", POLICY_TABLE));
|
||||||
|
|
||||||
MagiskManager.get().deleteDatabase("sulog.db");
|
Data.MM().deleteDatabase("sulog.db");
|
||||||
++oldVersion;
|
++oldVersion;
|
||||||
}
|
}
|
||||||
if (oldVersion == 2) {
|
if (oldVersion == 2) {
|
||||||
@@ -138,11 +138,16 @@ public class MagiskDatabaseHelper {
|
|||||||
db.execSQL(Utils.fmt("UPDATE %s SET uid=uid%%100000", POLICY_TABLE));
|
db.execSQL(Utils.fmt("UPDATE %s SET uid=uid%%100000", POLICY_TABLE));
|
||||||
++oldVersion;
|
++oldVersion;
|
||||||
}
|
}
|
||||||
|
if (oldVersion == 5) {
|
||||||
|
setSettings(Const.Key.SU_FINGERPRINT,
|
||||||
|
Data.MM().prefs.getBoolean(Const.Key.SU_FINGERPRINT, false) ? 1 : 0);
|
||||||
|
++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) {
|
||||||
MagiskManager.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);
|
||||||
db.execSQL("DROP TABLE IF EXISTS " + SETTINGS_TABLE);
|
db.execSQL("DROP TABLE IF EXISTS " + SETTINGS_TABLE);
|
||||||
@@ -170,11 +175,16 @@ public class MagiskDatabaseHelper {
|
|||||||
"(key TEXT, value INT, PRIMARY KEY(key))");
|
"(key TEXT, value INT, PRIMARY KEY(key))");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void flush() {
|
||||||
|
db.close();
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
// Clear outdated logs
|
// Clear outdated logs
|
||||||
db.delete(LOG_TABLE, Utils.fmt("time < %d", System.currentTimeMillis() - MagiskManager.get().suLogTimeout * 86400000), null);
|
db.delete(LOG_TABLE, Utils.fmt("time < %d", System.currentTimeMillis() - Data.suLogTimeout * 86400000), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void deletePolicy(Policy policy) {
|
public void deletePolicy(Policy policy) {
|
||||||
@@ -236,7 +246,7 @@ public class MagiskDatabaseHelper {
|
|||||||
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")));
|
||||||
newString = DateFormat.getDateInstance(DateFormat.MEDIUM, MagiskManager.locale).format(date);
|
newString = DateFormat.getDateInstance(DateFormat.MEDIUM, LocaleManager.locale).format(date);
|
||||||
if (!TextUtils.equals(dateString, newString)) {
|
if (!TextUtils.equals(dateString, newString)) {
|
||||||
dateString = newString;
|
dateString = newString;
|
||||||
list = new ArrayList<>();
|
list = new ArrayList<>();
|
||||||
|
|||||||
@@ -5,25 +5,27 @@ import android.database.Cursor;
|
|||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.adapters.ReposAdapter;
|
||||||
import com.topjohnwu.magisk.container.Repo;
|
import com.topjohnwu.magisk.container.Repo;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
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;
|
||||||
private MagiskManager mm;
|
private MagiskManager mm;
|
||||||
|
private ReposAdapter adapter;
|
||||||
|
|
||||||
public RepoDatabaseHelper(Context context) {
|
public RepoDatabaseHelper(Context context) {
|
||||||
super(context, "repo.db", null, DATABASE_VER);
|
super(context, "repo.db", null, DATABASE_VER);
|
||||||
mm = Utils.getMagiskManager(context);
|
mm = Data.MM();
|
||||||
mDb = getWritableDatabase();
|
mDb = getWritableDatabase();
|
||||||
|
|
||||||
// Remove outdated repos
|
// Remove outdated repos
|
||||||
@@ -38,21 +40,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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,15 +58,17 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
public void clearRepo() {
|
public void clearRepo() {
|
||||||
mDb.delete(TABLE_NAME, null, null);
|
mDb.delete(TABLE_NAME, null, null);
|
||||||
|
notifyAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void removeRepo(String id) {
|
public void removeRepo(String id) {
|
||||||
mDb.delete(TABLE_NAME, "id=?", new String[] { id });
|
mDb.delete(TABLE_NAME, "id=?", new String[] { id });
|
||||||
|
notifyAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeRepo(Repo repo) {
|
public void removeRepo(Repo repo) {
|
||||||
mDb.delete(TABLE_NAME, "repo_name=?", new String[] { repo.getRepoName() });
|
removeRepo(repo.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeRepo(Iterable<String> list) {
|
public void removeRepo(Iterable<String> list) {
|
||||||
@@ -79,10 +76,12 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
if (id == null) continue;
|
if (id == null) continue;
|
||||||
mDb.delete(TABLE_NAME, "id=?", new String[] { id });
|
mDb.delete(TABLE_NAME, "id=?", new String[] { id });
|
||||||
}
|
}
|
||||||
|
notifyAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addRepo(Repo repo) {
|
public void addRepo(Repo repo) {
|
||||||
mDb.replace(TABLE_NAME, null, repo.getContentValues());
|
mDb.replace(TABLE_NAME, null, repo.getContentValues());
|
||||||
|
notifyAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Repo getRepo(String id) {
|
public Repo getRepo(String id) {
|
||||||
@@ -100,7 +99,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
|
|
||||||
public Cursor getRepoCursor() {
|
public Cursor getRepoCursor() {
|
||||||
String orderBy = null;
|
String orderBy = null;
|
||||||
switch (mm.repoOrder) {
|
switch (Data.repoOrder) {
|
||||||
case Const.Value.ORDER_NAME:
|
case Const.Value.ORDER_NAME:
|
||||||
orderBy = "name COLLATE NOCASE";
|
orderBy = "name COLLATE NOCASE";
|
||||||
break;
|
break;
|
||||||
@@ -108,7 +107,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(mm.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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,4 +120,18 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
|||||||
}
|
}
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerAdapter(ReposAdapter a) {
|
||||||
|
adapter = a;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterAdapter() {
|
||||||
|
adapter = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void notifyAdapter() {
|
||||||
|
if (adapter != null) {
|
||||||
|
Data.mainHandler.post(adapter::notifyDBChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
package com.topjohnwu.magisk;
|
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.google.android.material.tabs.TabLayout;
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.MainActivity;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.adapters.TabFragmentAdapter;
|
import com.topjohnwu.magisk.adapters.TabFragmentAdapter;
|
||||||
import com.topjohnwu.magisk.components.Fragment;
|
import com.topjohnwu.magisk.components.BaseFragment;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
|
|
||||||
|
import androidx.viewpager.widget.ViewPager;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.Unbinder;
|
|
||||||
|
|
||||||
public class LogFragment extends Fragment {
|
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;
|
||||||
@@ -28,13 +27,13 @@ public class LogFragment extends Fragment {
|
|||||||
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) getActivity()).toolbar.setElevation(0);
|
((MainActivity) requireActivity()).toolbar.setElevation(0);
|
||||||
|
|
||||||
TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager());
|
TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager());
|
||||||
|
|
||||||
if (!(Const.USER_ID > 0 && getApplication().multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
|
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));
|
||||||
@@ -45,11 +44,4 @@ public class LogFragment extends Fragment {
|
|||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unbinder.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk.fragments;
|
||||||
|
|
||||||
import android.app.NotificationManager;
|
import android.app.NotificationManager;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.content.pm.PackageInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.StringRes;
|
|
||||||
import android.support.v4.widget.SwipeRefreshLayout;
|
|
||||||
import android.support.v7.widget.CardView;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -18,35 +15,45 @@ import android.widget.ProgressBar;
|
|||||||
import android.widget.RelativeLayout;
|
import android.widget.RelativeLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.MainActivity;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.asyncs.CheckSafetyNet;
|
import com.topjohnwu.magisk.asyncs.CheckSafetyNet;
|
||||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
||||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
import com.topjohnwu.magisk.components.BaseActivity;
|
||||||
|
import com.topjohnwu.magisk.components.BaseFragment;
|
||||||
|
import com.topjohnwu.magisk.components.CustomAlertDialog;
|
||||||
|
import com.topjohnwu.magisk.components.EnvFixDialog;
|
||||||
import com.topjohnwu.magisk.components.ExpandableView;
|
import com.topjohnwu.magisk.components.ExpandableView;
|
||||||
import com.topjohnwu.magisk.components.Fragment;
|
import com.topjohnwu.magisk.components.MagiskInstallDialog;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.components.ManagerInstallDialog;
|
||||||
|
import com.topjohnwu.magisk.components.UninstallDialog;
|
||||||
|
import com.topjohnwu.magisk.utils.Download;
|
||||||
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
|
import com.topjohnwu.magisk.utils.ISafetyNetHelper;
|
||||||
import com.topjohnwu.magisk.utils.ShowUI;
|
|
||||||
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 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 Fragment
|
public class MagiskFragment extends BaseFragment
|
||||||
implements Topic.Subscriber, SwipeRefreshLayout.OnRefreshListener, ExpandableView {
|
implements SwipeRefreshLayout.OnRefreshListener, ExpandableView, Topic.Subscriber {
|
||||||
|
|
||||||
private Container expandableContainer = new Container();
|
private Container expandableContainer = new Container();
|
||||||
|
|
||||||
private MagiskManager mm;
|
|
||||||
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.magisk_update) RelativeLayout magiskUpdate;
|
@BindView(R.id.magisk_update) RelativeLayout magiskUpdate;
|
||||||
@BindView(R.id.magisk_update_icon) ImageView magiskUpdateIcon;
|
@BindView(R.id.magisk_update_icon) ImageView magiskUpdateIcon;
|
||||||
@@ -75,7 +82,7 @@ public class MagiskFragment extends Fragment
|
|||||||
@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)
|
||||||
@@ -84,19 +91,12 @@ public class MagiskFragment extends Fragment
|
|||||||
safetyNetProgress.setVisibility(View.VISIBLE);
|
safetyNetProgress.setVisibility(View.VISIBLE);
|
||||||
safetyNetRefreshIcon.setVisibility(View.GONE);
|
safetyNetRefreshIcon.setVisibility(View.GONE);
|
||||||
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
|
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
|
||||||
new CheckSafetyNet(getActivity()).exec();
|
new CheckSafetyNet(requireActivity()).exec();
|
||||||
collapse();
|
collapse();
|
||||||
};
|
};
|
||||||
if (!TextUtils.equals(mm.getPackageName(), Const.ORIG_PKG_NAME)) {
|
if (!CheckSafetyNet.dexPath.exists()) {
|
||||||
new AlertDialogBuilder(getActivity())
|
|
||||||
.setTitle(R.string.cannot_check_sn_title)
|
|
||||||
.setMessage(R.string.cannot_check_sn_notice)
|
|
||||||
.setCancelable(true)
|
|
||||||
.setPositiveButton(R.string.ok, null)
|
|
||||||
.show();
|
|
||||||
} else if (!CheckSafetyNet.dexPath.exists()) {
|
|
||||||
// Show dialog
|
// Show dialog
|
||||||
new AlertDialogBuilder(getActivity())
|
new CustomAlertDialog(requireActivity())
|
||||||
.setTitle(R.string.proprietary_title)
|
.setTitle(R.string.proprietary_title)
|
||||||
.setMessage(R.string.proprietary_notice)
|
.setMessage(R.string.proprietary_notice)
|
||||||
.setCancelable(true)
|
.setCancelable(true)
|
||||||
@@ -114,36 +114,35 @@ public class MagiskFragment extends Fragment
|
|||||||
shownDialog = true;
|
shownDialog = true;
|
||||||
|
|
||||||
// Show Manager update first
|
// Show Manager update first
|
||||||
if (mm.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
|
if (Data.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
|
||||||
ShowUI.managerInstallDialog(getActivity());
|
new ManagerInstallDialog((BaseActivity) requireActivity()).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
((NotificationManager) mm.getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll();
|
((NotificationManager) mm.getSystemService(Context.NOTIFICATION_SERVICE)).cancelAll();
|
||||||
ShowUI.magiskInstallDialog(getActivity());
|
new MagiskInstallDialog((BaseActivity) getActivity()).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnClick(R.id.uninstall_button)
|
@OnClick(R.id.uninstall_button)
|
||||||
void uninstall() {
|
void uninstall() {
|
||||||
ShowUI.uninstallDialog(getActivity());
|
new UninstallDialog(requireActivity()).show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
|
||||||
|
@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);
|
||||||
getActivity().setTitle(R.string.magisk);
|
requireActivity().setTitle(R.string.magisk);
|
||||||
|
|
||||||
mm = getApplication();
|
|
||||||
|
|
||||||
expandableContainer.expandLayout = expandLayout;
|
expandableContainer.expandLayout = expandLayout;
|
||||||
setupExpandable();
|
setupExpandable();
|
||||||
|
|
||||||
keepVerityChkbox.setChecked(mm.keepVerity);
|
keepVerityChkbox.setChecked(Data.keepVerity);
|
||||||
keepVerityChkbox.setOnCheckedChangeListener((view, checked) -> mm.keepVerity = checked);
|
keepVerityChkbox.setOnCheckedChangeListener((view, checked) -> Data.keepVerity = checked);
|
||||||
keepEncChkbox.setChecked(mm.keepEnc);
|
keepEncChkbox.setChecked(Data.keepEnc);
|
||||||
keepEncChkbox.setOnCheckedChangeListener((view, checked) -> mm.keepEnc = checked);
|
keepEncChkbox.setOnCheckedChangeListener((view, checked) -> Data.keepEnc = checked);
|
||||||
|
|
||||||
mSwipeRefreshLayout.setOnRefreshListener(this);
|
mSwipeRefreshLayout.setOnRefreshListener(this);
|
||||||
updateUI();
|
updateUI();
|
||||||
@@ -153,7 +152,7 @@ public class MagiskFragment extends Fragment
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRefresh() {
|
public void onRefresh() {
|
||||||
mm.loadMagiskInfo();
|
Data.loadMagiskInfo();
|
||||||
updateUI();
|
updateUI();
|
||||||
|
|
||||||
magiskUpdateText.setText(R.string.checking_for_updates);
|
magiskUpdateText.setText(R.string.checking_for_updates);
|
||||||
@@ -162,69 +161,75 @@ public class MagiskFragment extends Fragment
|
|||||||
|
|
||||||
safetyNetStatusText.setText(R.string.safetyNet_check_text);
|
safetyNetStatusText.setText(R.string.safetyNet_check_text);
|
||||||
|
|
||||||
mm.safetyNetDone.reset();
|
Topic.reset(getSubscribedTopics());
|
||||||
mm.updateCheckDone.reset();
|
Data.remoteMagiskVersionString = null;
|
||||||
mm.remoteMagiskVersionString = null;
|
Data.remoteMagiskVersionCode = -1;
|
||||||
mm.remoteMagiskVersionCode = -1;
|
|
||||||
collapse();
|
collapse();
|
||||||
|
|
||||||
shownDialog = false;
|
shownDialog = false;
|
||||||
|
|
||||||
// Trigger state check
|
// Trigger state check
|
||||||
if (Utils.checkNetworkStatus()) {
|
if (Download.checkNetworkStatus(mm)) {
|
||||||
new CheckUpdates().exec();
|
CheckUpdates.check();
|
||||||
} else {
|
} else {
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTopicPublished(Topic topic) {
|
public int[] getSubscribedTopics() {
|
||||||
if (topic == mm.updateCheckDone) {
|
return new int[] {Topic.SNET_CHECK_DONE, Topic.UPDATE_CHECK_DONE};
|
||||||
updateCheckUI();
|
}
|
||||||
} else if (topic == mm.safetyNetDone) {
|
|
||||||
updateSafetyNetUI((int) topic.getResults()[0]);
|
@Override
|
||||||
|
public void onPublish(int topic, Object[] result) {
|
||||||
|
switch (topic) {
|
||||||
|
case Topic.SNET_CHECK_DONE:
|
||||||
|
updateSafetyNetUI((int) result[0]);
|
||||||
|
break;
|
||||||
|
case Topic.UPDATE_CHECK_DONE:
|
||||||
|
updateCheckUI();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Topic[] getSubscription() {
|
|
||||||
return new Topic[] { mm.updateCheckDone, mm.safetyNetDone };
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unbinder.unbind();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Container getContainer() {
|
public Container getContainer() {
|
||||||
return expandableContainer;
|
return expandableContainer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUI() {
|
private boolean hasGms() {
|
||||||
((MainActivity) getActivity()).checkHideSection();
|
PackageManager pm = mm.getPackageManager();
|
||||||
|
PackageInfo info;
|
||||||
|
try {
|
||||||
|
info = pm.getPackageInfo("com.google.android.gms", 0);
|
||||||
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return info.applicationInfo.enabled;
|
||||||
|
}
|
||||||
|
|
||||||
boolean hasNetwork = Utils.checkNetworkStatus();
|
private void updateUI() {
|
||||||
|
((MainActivity) requireActivity()).checkHideSection();
|
||||||
|
|
||||||
|
boolean hasNetwork = Download.checkNetworkStatus(mm);
|
||||||
boolean hasRoot = Shell.rootAccess();
|
boolean hasRoot = Shell.rootAccess();
|
||||||
boolean isUpToDate = mm.magiskVersionCode > Const.MAGISK_VER.UNIFIED;
|
|
||||||
|
|
||||||
magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
|
magiskUpdate.setVisibility(hasNetwork ? View.VISIBLE : View.GONE);
|
||||||
safetyNetCard.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);
|
||||||
|
|
||||||
int image, color;
|
int image, color;
|
||||||
|
|
||||||
if (mm.magiskVersionCode < 0) {
|
if (Data.magiskVersionCode < 0) {
|
||||||
color = colorBad;
|
color = colorBad;
|
||||||
image = R.drawable.ic_cancel;
|
image = R.drawable.ic_cancel;
|
||||||
magiskVersionText.setText(R.string.magisk_version_error);
|
magiskVersionText.setText(R.string.magisk_version_error);
|
||||||
} else {
|
} else {
|
||||||
color = colorOK;
|
color = colorOK;
|
||||||
image = R.drawable.ic_check_circle;
|
image = R.drawable.ic_check_circle;
|
||||||
magiskVersionText.setText(getString(R.string.current_magisk_title, "v" + mm.magiskVersionString));
|
magiskVersionText.setText(getString(R.string.current_magisk_title, "v" + Data.magiskVersionString));
|
||||||
}
|
}
|
||||||
|
|
||||||
magiskStatusIcon.setImageResource(image);
|
magiskStatusIcon.setImageResource(image);
|
||||||
@@ -234,7 +239,9 @@ public class MagiskFragment extends Fragment
|
|||||||
private void updateCheckUI() {
|
private void updateCheckUI() {
|
||||||
int image, color;
|
int image, color;
|
||||||
|
|
||||||
if (mm.remoteMagiskVersionCode < 0) {
|
safetyNetCard.setVisibility(hasGms() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
|
if (Data.remoteMagiskVersionCode < 0) {
|
||||||
color = colorNeutral;
|
color = colorNeutral;
|
||||||
image = R.drawable.ic_help;
|
image = R.drawable.ic_help;
|
||||||
magiskUpdateText.setText(R.string.invalid_update_channel);
|
magiskUpdateText.setText(R.string.invalid_update_channel);
|
||||||
@@ -242,11 +249,11 @@ public class MagiskFragment extends Fragment
|
|||||||
} else {
|
} else {
|
||||||
color = colorOK;
|
color = colorOK;
|
||||||
image = R.drawable.ic_check_circle;
|
image = R.drawable.ic_check_circle;
|
||||||
magiskUpdateText.setText(getString(R.string.install_magisk_title, "v" + mm.remoteMagiskVersionString));
|
magiskUpdateText.setText(getString(R.string.install_magisk_title, "v" + Data.remoteMagiskVersionString));
|
||||||
installButton.setVisibility(View.VISIBLE);
|
installButton.setVisibility(View.VISIBLE);
|
||||||
if (mm.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
|
if (Data.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
|
||||||
installText.setText(getString(R.string.update, getString(R.string.app_name)));
|
installText.setText(getString(R.string.update, getString(R.string.app_name)));
|
||||||
} else if (mm.magiskVersionCode > 0 && mm.remoteMagiskVersionCode > mm.magiskVersionCode) {
|
} else if (Data.magiskVersionCode > 0 && Data.remoteMagiskVersionCode > Data.magiskVersionCode) {
|
||||||
installText.setText(getString(R.string.update, getString(R.string.magisk)));
|
installText.setText(getString(R.string.update, getString(R.string.magisk)));
|
||||||
} else {
|
} else {
|
||||||
installText.setText(R.string.install);
|
installText.setText(R.string.install);
|
||||||
@@ -261,12 +268,12 @@ public class MagiskFragment extends Fragment
|
|||||||
mSwipeRefreshLayout.setRefreshing(false);
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
|
|
||||||
if (!shownDialog) {
|
if (!shownDialog) {
|
||||||
if (mm.remoteMagiskVersionCode > mm.magiskVersionCode
|
if (Data.remoteMagiskVersionCode > Data.magiskVersionCode
|
||||||
|| mm.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
|
|| Data.remoteManagerVersionCode > BuildConfig.VERSION_CODE) {
|
||||||
install();
|
install();
|
||||||
} else if (mm.remoteMagiskVersionCode >= Const.MAGISK_VER.FIX_ENV &&
|
} else if (Data.remoteMagiskVersionCode >= Const.MAGISK_VER.FIX_ENV &&
|
||||||
!ShellUtils.fastCmdResult("env_check")) {
|
!ShellUtils.fastCmdResult("env_check")) {
|
||||||
ShowUI.envFixDialog(getActivity());
|
new EnvFixDialog(requireActivity()).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -292,12 +299,6 @@ public class MagiskFragment extends Fragment
|
|||||||
} else {
|
} else {
|
||||||
@StringRes int resid;
|
@StringRes int resid;
|
||||||
switch (response) {
|
switch (response) {
|
||||||
case ISafetyNetHelper.CAUSE_SERVICE_DISCONNECTED:
|
|
||||||
resid = R.string.safetyNet_network_loss;
|
|
||||||
break;
|
|
||||||
case ISafetyNetHelper.CAUSE_NETWORK_LOST:
|
|
||||||
resid = R.string.safetyNet_service_disconnected;
|
|
||||||
break;
|
|
||||||
case ISafetyNetHelper.RESPONSE_ERR:
|
case ISafetyNetHelper.RESPONSE_ERR:
|
||||||
resid = R.string.safetyNet_res_invalid;
|
resid = R.string.safetyNet_res_invalid;
|
||||||
break;
|
break;
|
||||||
@@ -1,9 +1,6 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk.fragments;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
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;
|
||||||
@@ -11,19 +8,22 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.SearchView;
|
import android.widget.SearchView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
|
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
|
||||||
import com.topjohnwu.magisk.components.Fragment;
|
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 Fragment 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;
|
||||||
|
|
||||||
private ApplicationAdapter appAdapter;
|
private ApplicationAdapter appAdapter;
|
||||||
|
|
||||||
@@ -37,15 +37,15 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(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());
|
||||||
|
recyclerView.setAdapter(appAdapter);
|
||||||
|
|
||||||
mSwipeRefreshLayout.setRefreshing(true);
|
mSwipeRefreshLayout.setRefreshing(true);
|
||||||
mSwipeRefreshLayout.setOnRefreshListener(() -> appAdapter.refresh());
|
mSwipeRefreshLayout.setOnRefreshListener(appAdapter::refresh);
|
||||||
|
|
||||||
appAdapter = new ApplicationAdapter();
|
|
||||||
recyclerView.setAdapter(appAdapter);
|
|
||||||
|
|
||||||
searchListener = new SearchView.OnQueryTextListener() {
|
searchListener = new SearchView.OnQueryTextListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -61,7 +61,7 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
getActivity().setTitle(R.string.magiskhide);
|
requireActivity().setTitle(R.string.magiskhide);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
@@ -69,24 +69,18 @@ public class MagiskHideFragment extends Fragment implements Topic.Subscriber {
|
|||||||
@Override
|
@Override
|
||||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
inflater.inflate(R.menu.menu_magiskhide, menu);
|
inflater.inflate(R.menu.menu_magiskhide, menu);
|
||||||
SearchView search = (SearchView) menu.findItem(R.id.app_search).getActionView();
|
search = (SearchView) menu.findItem(R.id.app_search).getActionView();
|
||||||
search.setOnQueryTextListener(searchListener);
|
search.setOnQueryTextListener(searchListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public int[] getSubscribedTopics() {
|
||||||
super.onDestroyView();
|
return new int[] {Topic.MAGISK_HIDE_DONE};
|
||||||
unbinder.unbind();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTopicPublished(Topic topic) {
|
public void onPublish(int topic, Object[] result) {
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
appAdapter.filter(null);
|
appAdapter.filter(search.getQuery().toString());
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Topic[] getSubscription() {
|
|
||||||
return new Topic[] { getApplication().magiskHideDone };
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk.fragments;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
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;
|
||||||
@@ -17,25 +14,23 @@ import android.widget.ProgressBar;
|
|||||||
import android.widget.ScrollView;
|
import android.widget.ScrollView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.components.Fragment;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.magisk.components.BaseFragment;
|
||||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
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 com.topjohnwu.superuser.ShellUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
import butterknife.BindView;
|
import butterknife.BindView;
|
||||||
import butterknife.ButterKnife;
|
|
||||||
import butterknife.Unbinder;
|
|
||||||
|
|
||||||
public class MagiskLogFragment extends Fragment {
|
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;
|
||||||
@@ -46,7 +41,7 @@ public class MagiskLogFragment extends Fragment {
|
|||||||
@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;
|
||||||
@@ -64,12 +59,6 @@ public class MagiskLogFragment extends Fragment {
|
|||||||
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);
|
||||||
@@ -92,54 +81,39 @@ public class MagiskLogFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readLogs() {
|
private void readLogs() {
|
||||||
Shell.Async.su(new Shell.Async.Callback() {
|
Shell.su("cat " + Const.MAGISK_LOG + " | tail -n 5000").submit(result -> {
|
||||||
@Override
|
progressBar.setVisibility(View.GONE);
|
||||||
public void onTaskResult(@Nullable List<String> out, @Nullable List<String> err) {
|
if (result.getOut().isEmpty())
|
||||||
progressBar.setVisibility(View.GONE);
|
|
||||||
if (ShellUtils.isValidOutput(out)) {
|
|
||||||
txtLog.setText(TextUtils.join("\n", out));
|
|
||||||
} else {
|
|
||||||
txtLog.setText(R.string.log_is_empty);
|
|
||||||
}
|
|
||||||
svLog.postDelayed(() -> svLog.fullScroll(ScrollView.FOCUS_DOWN), 100);
|
|
||||||
hsvLog.postDelayed(() -> hsvLog.fullScroll(ScrollView.FOCUS_LEFT), 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTaskError(@NonNull Throwable throwable) {
|
|
||||||
txtLog.setText(R.string.log_is_empty);
|
txtLog.setText(R.string.log_is_empty);
|
||||||
}
|
else
|
||||||
}, "cat " + Const.MAGISK_LOG + " | tail -n 5000");
|
txtLog.setText(TextUtils.join("\n", result.getOut()));
|
||||||
|
svLog.postDelayed(() -> svLog.fullScroll(ScrollView.FOCUS_DOWN), 100);
|
||||||
|
hsvLog.postDelayed(() -> hsvLog.fullScroll(ScrollView.FOCUS_LEFT), 100);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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 targetFile = new File(Const.EXTERNAL_PATH + "/logs", filename);
|
File logFile = new File(Download.EXTERNAL_PATH, filename);
|
||||||
targetFile.getParentFile().mkdirs();
|
|
||||||
try {
|
try {
|
||||||
targetFile.createNewFile();
|
logFile.createNewFile();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Shell.Async.su(new Shell.Async.Callback() {
|
Shell.su("cat " + Const.MAGISK_LOG + " > " + logFile)
|
||||||
@Override
|
.submit(result ->
|
||||||
public void onTaskResult(@Nullable List<String> out, @Nullable List<String> err) {
|
SnackbarMaker.make(txtLog, logFile.getPath(), Snackbar.LENGTH_SHORT).show());
|
||||||
SnackbarMaker.make(txtLog, targetFile.getPath(), Snackbar.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTaskError(@NonNull Throwable throwable) {}
|
|
||||||
}, "cat " + Const.MAGISK_LOG + " > " + targetFile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearLogs() {
|
private void clearLogs() {
|
||||||
Shell.Async.su("echo -n > " + Const.MAGISK_LOG);
|
Shell.su("echo -n > " + Const.MAGISK_LOG).submit();
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,9 @@
|
|||||||
package com.topjohnwu.magisk;
|
package com.topjohnwu.magisk.fragments;
|
||||||
|
|
||||||
import android.Manifest;
|
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.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;
|
||||||
@@ -15,30 +12,36 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.FlashActivity;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.adapters.ModulesAdapter;
|
import com.topjohnwu.magisk.adapters.ModulesAdapter;
|
||||||
import com.topjohnwu.magisk.asyncs.LoadModules;
|
import com.topjohnwu.magisk.components.BaseFragment;
|
||||||
import com.topjohnwu.magisk.components.Fragment;
|
|
||||||
import com.topjohnwu.magisk.container.Module;
|
import com.topjohnwu.magisk.container.Module;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
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;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
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 Fragment 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");
|
||||||
@@ -50,14 +53,14 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(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(() -> {
|
||||||
recyclerView.setVisibility(View.GONE);
|
recyclerView.setVisibility(View.GONE);
|
||||||
new LoadModules().exec();
|
Utils.loadModules();
|
||||||
});
|
});
|
||||||
|
|
||||||
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||||
@@ -72,37 +75,31 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
getActivity().setTitle(R.string.modules);
|
requireActivity().setTitle(R.string.modules);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTopicPublished(Topic topic) {
|
public int[] getSubscribedTopics() {
|
||||||
updateUI();
|
return new int[] {Topic.MODULE_LOAD_DONE};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Topic[] getSubscription() {
|
public void onPublish(int topic, Object[] result) {
|
||||||
return new Topic[] { getApplication().moduleLoadDone };
|
updateUI((Map<String, Module>) result[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
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);
|
||||||
@@ -112,25 +109,25 @@ public class ModulesFragment extends Fragment implements Topic.Subscriber {
|
|||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.reboot:
|
case R.id.reboot:
|
||||||
Shell.Async.su("/system/bin/reboot");
|
Shell.su("/system/bin/reboot").submit();
|
||||||
return true;
|
return true;
|
||||||
case R.id.reboot_recovery:
|
case R.id.reboot_recovery:
|
||||||
Shell.Async.su("/system/bin/reboot recovery");
|
Shell.su("/system/bin/reboot recovery").submit();
|
||||||
return true;
|
return true;
|
||||||
case R.id.reboot_bootloader:
|
case R.id.reboot_bootloader:
|
||||||
Shell.Async.su("/system/bin/reboot bootloader");
|
Shell.su("/system/bin/reboot bootloader").submit();
|
||||||
return true;
|
return true;
|
||||||
case R.id.reboot_download:
|
case R.id.reboot_download:
|
||||||
Shell.Async.su("/system/bin/reboot download");
|
Shell.su("/system/bin/reboot download").submit();
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateUI() {
|
private void updateUI(Map<String, Module> moduleMap) {
|
||||||
listModules.clear();
|
listModules.clear();
|
||||||
listModules.addAll(getApplication().moduleMap.values());
|
listModules.addAll(moduleMap.values());
|
||||||
if (listModules.size() == 0) {
|
if (listModules.size() == 0) {
|
||||||
emptyRv.setVisibility(View.VISIBLE);
|
emptyRv.setVisibility(View.VISIBLE);
|
||||||
recyclerView.setVisibility(View.GONE);
|
recyclerView.setVisibility(View.GONE);
|
||||||
@@ -1,10 +1,7 @@
|
|||||||
package com.topjohnwu.magisk;
|
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.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;
|
||||||
@@ -14,25 +11,30 @@ import android.view.ViewGroup;
|
|||||||
import android.widget.SearchView;
|
import android.widget.SearchView;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.adapters.ReposAdapter;
|
import com.topjohnwu.magisk.adapters.ReposAdapter;
|
||||||
import com.topjohnwu.magisk.asyncs.UpdateRepos;
|
import com.topjohnwu.magisk.asyncs.UpdateRepos;
|
||||||
import com.topjohnwu.magisk.components.Fragment;
|
import com.topjohnwu.magisk.components.BaseFragment;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.container.Module;
|
||||||
import com.topjohnwu.magisk.utils.Topic;
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
|
||||||
|
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 Fragment implements Topic.Subscriber {
|
public class ReposFragment extends BaseFragment implements Topic.Subscriber {
|
||||||
|
|
||||||
private Unbinder unbinder;
|
|
||||||
private MagiskManager mm;
|
|
||||||
@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;
|
||||||
|
|
||||||
public static ReposAdapter adapter;
|
private ReposAdapter adapter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
@@ -42,47 +44,43 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(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);
|
||||||
mm = getApplication();
|
|
||||||
|
|
||||||
mSwipeRefreshLayout.setRefreshing(mm.repoLoadDone.isPending());
|
mSwipeRefreshLayout.setRefreshing(true);
|
||||||
|
recyclerView.setVisibility(View.GONE);
|
||||||
|
|
||||||
mSwipeRefreshLayout.setOnRefreshListener(() -> {
|
mSwipeRefreshLayout.setOnRefreshListener(() -> {
|
||||||
recyclerView.setVisibility(View.VISIBLE);
|
recyclerView.setVisibility(View.VISIBLE);
|
||||||
emptyRv.setVisibility(View.GONE);
|
emptyRv.setVisibility(View.GONE);
|
||||||
new UpdateRepos(true).exec();
|
new UpdateRepos().exec(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
getActivity().setTitle(R.string.downloads);
|
requireActivity().setTitle(R.string.downloads);
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onResume() {
|
public int[] getSubscribedTopics() {
|
||||||
adapter = new ReposAdapter(mm.repoDB, mm.moduleMap);
|
return new int[] {Topic.MODULE_LOAD_DONE, Topic.REPO_LOAD_DONE};
|
||||||
recyclerView.setAdapter(adapter);
|
|
||||||
super.onResume();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPause() {
|
public void onPublish(int topic, Object[] result) {
|
||||||
super.onPause();
|
if (topic == Topic.MODULE_LOAD_DONE) {
|
||||||
adapter = null;
|
adapter = new ReposAdapter(mm.repoDB, (Map<String, Module>) result[0]);
|
||||||
}
|
mm.repoDB.registerAdapter(adapter);
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
@Override
|
recyclerView.setVisibility(View.VISIBLE);
|
||||||
public void onTopicPublished(Topic topic) {
|
emptyRv.setVisibility(View.GONE);
|
||||||
mSwipeRefreshLayout.setRefreshing(false);
|
}
|
||||||
recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE);
|
if (Topic.isPublished(getSubscribedTopics())) {
|
||||||
emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
mSwipeRefreshLayout.setRefreshing(false);
|
||||||
}
|
recyclerView.setVisibility(adapter.getItemCount() == 0 ? View.GONE : View.VISIBLE);
|
||||||
|
emptyRv.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
||||||
@Override
|
}
|
||||||
public Topic[] getSubscription() {
|
|
||||||
return new Topic[] { mm.repoLoadDone };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -108,9 +106,9 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
|
|||||||
if (item.getItemId() == R.id.repo_sort) {
|
if (item.getItemId() == R.id.repo_sort) {
|
||||||
new AlertDialog.Builder(getActivity())
|
new AlertDialog.Builder(getActivity())
|
||||||
.setTitle(R.string.sorting_order)
|
.setTitle(R.string.sorting_order)
|
||||||
.setSingleChoiceItems(R.array.sorting_orders, mm.repoOrder, (d, which) -> {
|
.setSingleChoiceItems(R.array.sorting_orders, Data.repoOrder, (d, which) -> {
|
||||||
mm.repoOrder = which;
|
Data.repoOrder = which;
|
||||||
mm.prefs.edit().putInt(Const.Key.REPO_ORDER, mm.repoOrder).apply();
|
mm.prefs.edit().putInt(Const.Key.REPO_ORDER, Data.repoOrder).apply();
|
||||||
adapter.notifyDBChanged();
|
adapter.notifyDBChanged();
|
||||||
d.dismiss();
|
d.dismiss();
|
||||||
}).show();
|
}).show();
|
||||||
@@ -121,6 +119,6 @@ public class ReposFragment extends Fragment implements Topic.Subscriber {
|
|||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
unbinder.unbind();
|
mm.repoDB.unregisterAdapter();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,342 @@
|
|||||||
|
package com.topjohnwu.magisk.fragments;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.EditText;
|
||||||
|
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.asyncs.CheckUpdates;
|
||||||
|
import com.topjohnwu.magisk.asyncs.PatchAPK;
|
||||||
|
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
||||||
|
import com.topjohnwu.magisk.utils.Download;
|
||||||
|
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
||||||
|
import com.topjohnwu.magisk.utils.LocaleManager;
|
||||||
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
|
import com.topjohnwu.magisk.utils.Topic;
|
||||||
|
import com.topjohnwu.magisk.utils.Utils;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
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
|
||||||
|
implements SharedPreferences.OnSharedPreferenceChangeListener,
|
||||||
|
Topic.Subscriber, Topic.AutoSubscriber {
|
||||||
|
|
||||||
|
private PreferenceScreen prefScreen;
|
||||||
|
|
||||||
|
private ListPreference updateChannel, suAccess, autoRes, suNotification,
|
||||||
|
requestTimeout, multiuserMode, namespaceMode;
|
||||||
|
private MagiskManager mm;
|
||||||
|
private PreferenceCategory generalCatagory;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
|
||||||
|
setPreferencesFromResource(R.xml.app_settings, rootKey);
|
||||||
|
mm = Data.MM();
|
||||||
|
prefScreen = getPreferenceScreen();
|
||||||
|
|
||||||
|
generalCatagory = (PreferenceCategory) findPreference("general");
|
||||||
|
PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk");
|
||||||
|
PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser");
|
||||||
|
Preference hideManager = findPreference("hide");
|
||||||
|
Preference restoreManager = findPreference("restore");
|
||||||
|
findPreference("clear").setOnPreferenceClickListener((pref) -> {
|
||||||
|
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
|
||||||
|
mm.repoDB.clearRepo();
|
||||||
|
Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT);
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
updateChannel = (ListPreference) findPreference(Const.Key.UPDATE_CHANNEL);
|
||||||
|
suAccess = (ListPreference) findPreference(Const.Key.ROOT_ACCESS);
|
||||||
|
autoRes = (ListPreference) findPreference(Const.Key.SU_AUTO_RESPONSE);
|
||||||
|
requestTimeout = (ListPreference) findPreference(Const.Key.SU_REQUEST_TIMEOUT);
|
||||||
|
suNotification = (ListPreference) findPreference(Const.Key.SU_NOTIFICATION);
|
||||||
|
multiuserMode = (ListPreference) findPreference(Const.Key.SU_MULTIUSER_MODE);
|
||||||
|
namespaceMode = (ListPreference) findPreference(Const.Key.SU_MNT_NS);
|
||||||
|
SwitchPreference reauth = (SwitchPreference) findPreference(Const.Key.SU_REAUTH);
|
||||||
|
SwitchPreference fingerprint = (SwitchPreference) findPreference(Const.Key.SU_FINGERPRINT);
|
||||||
|
|
||||||
|
updateChannel.setOnPreferenceChangeListener((p, o) -> {
|
||||||
|
String prev =String.valueOf(Data.updateChannel);
|
||||||
|
int channel = Integer.parseInt((String) o);
|
||||||
|
if (channel == Const.Value.CUSTOM_CHANNEL) {
|
||||||
|
View v = LayoutInflater.from(requireActivity()).inflate(R.layout.custom_channel_dialog, null);
|
||||||
|
EditText url = v.findViewById(R.id.custom_url);
|
||||||
|
url.setText(mm.prefs.getString(Const.Key.CUSTOM_CHANNEL, ""));
|
||||||
|
new AlertDialog.Builder(requireActivity())
|
||||||
|
.setTitle(R.string.settings_update_custom)
|
||||||
|
.setView(v)
|
||||||
|
.setPositiveButton(R.string.ok, (d, i) ->
|
||||||
|
mm.prefs.edit().putString(Const.Key.CUSTOM_CHANNEL,
|
||||||
|
url.getText().toString()).apply())
|
||||||
|
.setNegativeButton(R.string.close, (d, i) ->
|
||||||
|
mm.prefs.edit().putString(Const.Key.UPDATE_CHANNEL, prev).apply())
|
||||||
|
.setOnCancelListener(d ->
|
||||||
|
mm.prefs.edit().putString(Const.Key.UPDATE_CHANNEL, prev).apply())
|
||||||
|
.show();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
setSummary();
|
||||||
|
|
||||||
|
// Disable dangerous settings in secondary user
|
||||||
|
if (Const.USER_ID > 0) {
|
||||||
|
suCategory.removePreference(multiuserMode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable re-authentication option on Android O, it will not work
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
reauth.setEnabled(false);
|
||||||
|
reauth.setChecked(false);
|
||||||
|
reauth.setSummary(R.string.android_o_not_support);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable fingerprint option if not possible
|
||||||
|
if (!FingerprintHelper.canUseFingerprint()) {
|
||||||
|
fingerprint.setEnabled(false);
|
||||||
|
fingerprint.setChecked(false);
|
||||||
|
fingerprint.setSummary(R.string.disable_fingerprint);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Shell.rootAccess()) {
|
||||||
|
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
||||||
|
hideManager.setOnPreferenceClickListener((pref) -> {
|
||||||
|
PatchAPK.hideManager(requireActivity());
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
generalCatagory.removePreference(restoreManager);
|
||||||
|
} else {
|
||||||
|
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(hideManager);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
generalCatagory.removePreference(restoreManager);
|
||||||
|
generalCatagory.removePreference(hideManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Shell.rootAccess() || (Const.USER_ID > 0 &&
|
||||||
|
Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
|
||||||
|
prefScreen.removePreference(suCategory);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Shell.rootAccess()) {
|
||||||
|
prefScreen.removePreference(magiskCategory);
|
||||||
|
generalCatagory.removePreference(hideManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setLocalePreference(ListPreference lp) {
|
||||||
|
CharSequence[] entries = new CharSequence[LocaleManager.locales.size() + 1];
|
||||||
|
CharSequence[] entryValues = new CharSequence[LocaleManager.locales.size() + 1];
|
||||||
|
entries[0] = LocaleManager.getString(LocaleManager.defaultLocale, R.string.system_default);
|
||||||
|
entryValues[0] = "";
|
||||||
|
int i = 1;
|
||||||
|
for (Locale locale : LocaleManager.locales) {
|
||||||
|
entries[i] = locale.getDisplayName(locale);
|
||||||
|
entryValues[i++] = locale.toLanguageTag();
|
||||||
|
}
|
||||||
|
lp.setEntries(entries);
|
||||||
|
lp.setEntryValues(entryValues);
|
||||||
|
lp.setSummary(LocaleManager.locale.getDisplayName(LocaleManager.locale));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
mm.prefs.registerOnSharedPreferenceChangeListener(this);
|
||||||
|
Topic.subscribe(this);
|
||||||
|
requireActivity().setTitle(R.string.settings);
|
||||||
|
return super.onCreateView(inflater, container, savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDestroyView() {
|
||||||
|
mm.prefs.unregisterOnSharedPreferenceChangeListener(this);
|
||||||
|
Topic.unsubscribe(this);
|
||||||
|
super.onDestroyView();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
|
||||||
|
switch (key) {
|
||||||
|
case Const.Key.ROOT_ACCESS:
|
||||||
|
case Const.Key.SU_MULTIUSER_MODE:
|
||||||
|
case Const.Key.SU_MNT_NS:
|
||||||
|
mm.mDB.setSettings(key, Utils.getPrefsInt(prefs, key));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Data.loadConfig();
|
||||||
|
setSummary();
|
||||||
|
switch (key) {
|
||||||
|
case Const.Key.DARK_THEME:
|
||||||
|
requireActivity().recreate();
|
||||||
|
break;
|
||||||
|
case Const.Key.COREONLY:
|
||||||
|
if (prefs.getBoolean(key, false)) {
|
||||||
|
try {
|
||||||
|
Const.MAGISK_DISABLE_FILE.createNewFile();
|
||||||
|
} catch (IOException ignored) {}
|
||||||
|
} else {
|
||||||
|
Const.MAGISK_DISABLE_FILE.delete();
|
||||||
|
}
|
||||||
|
Utils.toast(R.string.settings_reboot_toast, Toast.LENGTH_LONG);
|
||||||
|
break;
|
||||||
|
case Const.Key.MAGISKHIDE:
|
||||||
|
if (prefs.getBoolean(key, false)) {
|
||||||
|
Shell.su("magiskhide --enable").submit();
|
||||||
|
} else {
|
||||||
|
Shell.su("magiskhide --disable").submit();
|
||||||
|
}
|
||||||
|
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:
|
||||||
|
LocaleManager.setLocale(mm);
|
||||||
|
requireActivity().recreate();
|
||||||
|
break;
|
||||||
|
case Const.Key.UPDATE_CHANNEL:
|
||||||
|
case Const.Key.CUSTOM_CHANNEL:
|
||||||
|
CheckUpdates.check();
|
||||||
|
break;
|
||||||
|
case Const.Key.CHECK_UPDATES:
|
||||||
|
Utils.setupUpdateCheck();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceTreeClick(Preference preference) {
|
||||||
|
String key = preference.getKey();
|
||||||
|
switch (key) {
|
||||||
|
case Const.Key.SU_FINGERPRINT:
|
||||||
|
boolean checked = ((SwitchPreference) preference).isChecked();
|
||||||
|
((SwitchPreference) preference).setChecked(!checked);
|
||||||
|
FingerprintHelper.showAuthDialog(requireActivity(), () -> {
|
||||||
|
((SwitchPreference) preference).setChecked(checked);
|
||||||
|
Data.suFingerprint = checked;
|
||||||
|
mm.mDB.setSettings(key, checked ? 1 : 0);
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSummary() {
|
||||||
|
updateChannel.setSummary(getResources()
|
||||||
|
.getStringArray(R.array.update_channel)[Data.updateChannel]);
|
||||||
|
suAccess.setSummary(getResources()
|
||||||
|
.getStringArray(R.array.su_access)[Data.suAccessState]);
|
||||||
|
autoRes.setSummary(getResources()
|
||||||
|
.getStringArray(R.array.auto_response)[Data.suResponseType]);
|
||||||
|
suNotification.setSummary(getResources()
|
||||||
|
.getStringArray(R.array.su_notification)[Data.suNotificationType]);
|
||||||
|
requestTimeout.setSummary(
|
||||||
|
getString(R.string.request_timeout_summary,
|
||||||
|
mm.prefs.getString(Const.Key.SU_REQUEST_TIMEOUT, "10")));
|
||||||
|
multiuserMode.setSummary(getResources()
|
||||||
|
.getStringArray(R.array.multiuser_summary)[Data.multiuserMode]);
|
||||||
|
namespaceMode.setSummary(getResources()
|
||||||
|
.getStringArray(R.array.namespace_summary)[Data.suNamespaceMode]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPublish(int topic, Object[] result) {
|
||||||
|
setLocalePreference((ListPreference) findPreference(Const.Key.LOCALE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int[] getSubscribedTopics() {
|
||||||
|
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;
|
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;
|
||||||
@@ -11,20 +9,19 @@ import android.view.View;
|
|||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.adapters.SuLogAdapter;
|
import com.topjohnwu.magisk.adapters.SuLogAdapter;
|
||||||
import com.topjohnwu.magisk.components.Fragment;
|
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 Fragment {
|
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 MagiskManager mm;
|
|
||||||
private SuLogAdapter adapter;
|
private SuLogAdapter adapter;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -44,8 +41,7 @@ public class SuLogFragment extends Fragment {
|
|||||||
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);
|
||||||
mm = getApplication();
|
|
||||||
adapter = new SuLogAdapter(mm.mDB);
|
adapter = new SuLogAdapter(mm.mDB);
|
||||||
recyclerView.setAdapter(adapter);
|
recyclerView.setAdapter(adapter);
|
||||||
|
|
||||||
@@ -80,10 +76,4 @@ public class SuLogFragment extends Fragment {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unbinder.unbind();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,39 +1,53 @@
|
|||||||
package com.topjohnwu.magisk;
|
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;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.adapters.PolicyAdapter;
|
import com.topjohnwu.magisk.adapters.PolicyAdapter;
|
||||||
import com.topjohnwu.magisk.components.Fragment;
|
import com.topjohnwu.magisk.components.BaseFragment;
|
||||||
import com.topjohnwu.magisk.container.Policy;
|
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 Fragment {
|
public class SuperuserFragment extends BaseFragment {
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
PackageManager pm = getActivity().getPackageManager();
|
pm = requireActivity().getPackageManager();
|
||||||
MagiskManager mm = getApplication();
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
requireActivity().setTitle(getString(R.string.superuser));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
displayPolicyList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void displayPolicyList() {
|
||||||
List<Policy> policyList = mm.mDB.getPolicyList(pm);
|
List<Policy> policyList = mm.mDB.getPolicyList(pm);
|
||||||
|
|
||||||
if (policyList.size() == 0) {
|
if (policyList.size() == 0) {
|
||||||
@@ -44,20 +58,6 @@ public class SuperuserFragment extends Fragment {
|
|||||||
emptyRv.setVisibility(View.GONE);
|
emptyRv.setVisibility(View.GONE);
|
||||||
recyclerView.setVisibility(View.VISIBLE);
|
recyclerView.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onStart() {
|
|
||||||
super.onStart();
|
|
||||||
getActivity().setTitle(getString(R.string.superuser));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroyView() {
|
|
||||||
super.onDestroyView();
|
|
||||||
unbinder.unbind();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -3,19 +3,33 @@ package com.topjohnwu.magisk.receivers;
|
|||||||
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.os.Build;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.services.OnBootIntentService;
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.SuRequestActivity;
|
||||||
|
import com.topjohnwu.magisk.services.OnBootService;
|
||||||
|
import com.topjohnwu.magisk.utils.SuConnector;
|
||||||
|
|
||||||
public class BootReceiver extends BroadcastReceiver {
|
public class BootReceiver extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (TextUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED)) {
|
||||||
context.startForegroundService(new Intent(context, OnBootIntentService.class));
|
switch (intent.getExtras().getString("action", "boot")) {
|
||||||
} else {
|
case "request":
|
||||||
context.startService(new Intent(context, OnBootIntentService.class));
|
Intent i = new Intent(context, Data.classMap.get(SuRequestActivity.class))
|
||||||
|
.putExtra("socket", intent.getStringExtra("socket"))
|
||||||
|
.putExtra("version", 2)
|
||||||
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
context.startActivity(i);
|
||||||
|
break;
|
||||||
|
case "log":
|
||||||
|
SuConnector.handleLogs(intent, 2);
|
||||||
|
break;
|
||||||
|
case "boot":
|
||||||
|
OnBootService.enqueueWork(context);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,20 +6,21 @@ import android.content.Intent;
|
|||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
import com.topjohnwu.magisk.Const;
|
||||||
import com.topjohnwu.magisk.utils.PatchAPK;
|
import com.topjohnwu.magisk.asyncs.PatchAPK;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.Download;
|
||||||
|
import com.topjohnwu.utils.JarMap;
|
||||||
|
import com.topjohnwu.utils.SignAPK;
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
|
|
||||||
public class ManagerUpdate extends BroadcastReceiver {
|
public class ManagerUpdate extends BroadcastReceiver {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
Utils.dlAndReceive(
|
Download.receive(
|
||||||
context, new PatchedInstall(),
|
context, new PatchedInstall(),
|
||||||
intent.getStringExtra(Const.Key.INTENT_SET_LINK),
|
intent.getStringExtra(Const.Key.INTENT_SET_LINK),
|
||||||
intent.getStringExtra(Const.Key.INTENT_SET_FILENAME)
|
intent.getStringExtra(Const.Key.INTENT_SET_FILENAME)
|
||||||
@@ -31,13 +32,14 @@ public class ManagerUpdate extends BroadcastReceiver {
|
|||||||
public void onDownloadDone(Context context, Uri uri) {
|
public void onDownloadDone(Context context, Uri uri) {
|
||||||
if (!context.getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
if (!context.getPackageName().equals(Const.ORIG_PKG_NAME)) {
|
||||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
String o = uri.getPath();
|
String orig = uri.getPath();
|
||||||
String p = o.substring(0, o.lastIndexOf('.')) + "-patched.apk";
|
String patch = orig.substring(0, orig.lastIndexOf('.')) + "-patched.apk";
|
||||||
try {
|
try {
|
||||||
PatchAPK.patchPackageID(o, new BufferedOutputStream(new FileOutputStream(p)),
|
JarMap apk = new JarMap(orig);
|
||||||
Const.ORIG_PKG_NAME, context.getPackageName());
|
PatchAPK.patchPackageID(apk, Const.ORIG_PKG_NAME, context.getPackageName());
|
||||||
} catch (FileNotFoundException ignored) { }
|
SignAPK.sign(apk, new BufferedOutputStream(new FileOutputStream(patch)));
|
||||||
super.onDownloadDone(context, Uri.fromFile(new File(p)));
|
super.onDownloadDone(context, Uri.fromFile(new File(patch)));
|
||||||
|
} catch (Exception ignored) { }
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
super.onDownloadDone(context, uri);
|
super.onDownloadDone(context, uri);
|
||||||
|
|||||||
@@ -4,15 +4,15 @@ import android.content.BroadcastReceiver;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
public class PackageReceiver extends BroadcastReceiver {
|
public class PackageReceiver extends BroadcastReceiver {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
MagiskManager mm = Utils.getMagiskManager(context);
|
MagiskManager mm = Data.MM();
|
||||||
|
|
||||||
String pkg = intent.getData().getEncodedSchemeSpecificPart();
|
String pkg = intent.getData().getEncodedSchemeSpecificPart();
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ public class PackageReceiver extends BroadcastReceiver {
|
|||||||
break;
|
break;
|
||||||
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
|
case Intent.ACTION_PACKAGE_FULLY_REMOVED:
|
||||||
mm.mDB.deletePolicy(pkg);
|
mm.mDB.deletePolicy(pkg);
|
||||||
Shell.Async.su("magiskhide --rm " + pkg);
|
Shell.su("magiskhide --rm " + pkg).submit();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,6 @@ import com.topjohnwu.superuser.Shell;
|
|||||||
public class RebootReceiver extends BroadcastReceiver {
|
public class RebootReceiver extends BroadcastReceiver {
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
Shell.Async.su("/system/bin/reboot");
|
Shell.su("/system/bin/reboot").submit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,28 +7,24 @@ 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 android.text.TextUtils;
|
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
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.Const;
|
|
||||||
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) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
|
||||||
MagiskManager mm = Utils.getMagiskManager(context);
|
MagiskManager mm = Data.MM();
|
||||||
ShortcutManager manager = context.getSystemService(ShortcutManager.class);
|
ShortcutManager manager = context.getSystemService(ShortcutManager.class);
|
||||||
if (TextUtils.equals(intent.getAction(), Intent.ACTION_LOCALE_CHANGED)) {
|
|
||||||
// It is triggered with locale change, manual load Magisk info
|
|
||||||
mm.loadMagiskInfo();
|
|
||||||
}
|
|
||||||
manager.setDynamicShortcuts(getShortCuts(mm));
|
manager.setDynamicShortcuts(getShortCuts(mm));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,12 +32,12 @@ public class ShortcutReceiver extends BroadcastReceiver {
|
|||||||
@RequiresApi(api = Build.VERSION_CODES.N_MR1)
|
@RequiresApi(api = Build.VERSION_CODES.N_MR1)
|
||||||
private ArrayList<ShortcutInfo> getShortCuts(MagiskManager mm) {
|
private ArrayList<ShortcutInfo> getShortCuts(MagiskManager mm) {
|
||||||
ArrayList<ShortcutInfo> shortCuts = new ArrayList<>();
|
ArrayList<ShortcutInfo> shortCuts = new ArrayList<>();
|
||||||
if (Shell.rootAccess() &&
|
boolean root = Shell.rootAccess();
|
||||||
!(Const.USER_ID > 0 &&
|
if (root && !(Const.USER_ID > 0 &&
|
||||||
mm.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
|
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))
|
||||||
@@ -49,11 +45,10 @@ public class ShortcutReceiver extends BroadcastReceiver {
|
|||||||
.setRank(0)
|
.setRank(0)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
if (Shell.rootAccess() && mm.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))
|
||||||
@@ -61,11 +56,10 @@ public class ShortcutReceiver extends BroadcastReceiver {
|
|||||||
.setRank(1)
|
.setRank(1)
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
if (!mm.prefs.getBoolean(Const.Key.COREONLY, false) &&
|
if (!mm.prefs.getBoolean(Const.Key.COREONLY, false) && root && Data.magiskVersionCode >= 0) {
|
||||||
Shell.rootAccess() && mm.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))
|
||||||
@@ -74,7 +68,7 @@ public class ShortcutReceiver extends BroadcastReceiver {
|
|||||||
.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.download))
|
||||||
.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))
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.services;
|
|
||||||
|
|
||||||
import android.app.IntentService;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.support.v4.app.NotificationCompat;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
import com.topjohnwu.magisk.utils.RootUtils;
|
|
||||||
|
|
||||||
public class OnBootIntentService extends IntentService {
|
|
||||||
|
|
||||||
public OnBootIntentService() {
|
|
||||||
super("OnBootIntentService");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCreate() {
|
|
||||||
super.onCreate();
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
||||||
startForeground(Const.ID.ONBOOT_NOTIFICATION_ID,
|
|
||||||
new NotificationCompat.Builder(this, Const.ID.NOTIFICATION_CHANNEL)
|
|
||||||
.setSmallIcon(R.drawable.ic_magisk_outline)
|
|
||||||
.setContentTitle("Startup Operations")
|
|
||||||
.setContentText("Running startup operations...")
|
|
||||||
.build());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onHandleIntent(Intent intent) {
|
|
||||||
/* Pixel 2 (XL) devices will need to patch dtbo.img.
|
|
||||||
* However, that is not possible if Magisk is installed by
|
|
||||||
* patching boot image with Magisk Manager and fastboot flash
|
|
||||||
* the boot image, since at that time we do not have root.
|
|
||||||
* Check for dtbo status every boot time, and prompt user
|
|
||||||
* to reboot if dtbo wasn't patched and patched by Magisk Manager.
|
|
||||||
* */
|
|
||||||
MagiskManager.get().loadMagiskInfo();
|
|
||||||
RootUtils.patchDTBO();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
package com.topjohnwu.magisk.services;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.utils.NotificationMgr;
|
||||||
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.core.app.JobIntentService;
|
||||||
|
|
||||||
|
public class OnBootService extends JobIntentService {
|
||||||
|
|
||||||
|
public static void enqueueWork(Context context) {
|
||||||
|
enqueueWork(context, Data.classMap.get(OnBootService.class), Const.ID.ONBOOT_SERVICE_ID, new Intent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onHandleWork(@NonNull Intent intent) {
|
||||||
|
/* Devices with DTBO might want to patch dtbo.img.
|
||||||
|
* However, that is not possible if Magisk is installed by
|
||||||
|
* patching boot image with Magisk Manager and flashed via
|
||||||
|
* fastboot, since at that time we do not have root.
|
||||||
|
* Check for dtbo status every boot time, and prompt user
|
||||||
|
* to reboot if dtbo wasn't patched and patched by Magisk Manager.
|
||||||
|
* */
|
||||||
|
if (Shell.rootAccess() && ShellUtils.fastCmdResult("mm_patch_dtbo"))
|
||||||
|
NotificationMgr.dtboPatched();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,15 +4,14 @@ import android.app.job.JobParameters;
|
|||||||
import android.app.job.JobService;
|
import android.app.job.JobService;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.superuser.Shell;
|
||||||
|
|
||||||
public class UpdateCheckService extends JobService {
|
public class UpdateCheckService extends JobService {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onStartJob(JobParameters params) {
|
public boolean onStartJob(JobParameters params) {
|
||||||
Utils.getMagiskManager(this).loadMagiskInfo();
|
Shell.getShell();
|
||||||
new CheckUpdates(true)
|
CheckUpdates.check(() -> jobFinished(params, false));
|
||||||
.setCallBack(() -> jobFinished(params, false)).exec();
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,300 +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.MagiskManager;
|
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.Activity;
|
|
||||||
import com.topjohnwu.magisk.container.Policy;
|
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
import com.topjohnwu.magisk.utils.FingerprintHelper;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
public class RequestActivity extends BaseActivity {
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import butterknife.BindView;
|
|
||||||
import butterknife.ButterKnife;
|
|
||||||
|
|
||||||
public class RequestActivity extends Activity {
|
|
||||||
|
|
||||||
@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 MagiskManager mm;
|
|
||||||
|
|
||||||
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 = Utils.getMagiskManager(this);
|
startActivity(intent);
|
||||||
mm.mDB.clearOutdated();
|
|
||||||
|
|
||||||
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 (mm.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(mm.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 = mm.prefs.getBoolean(Const.Key.SU_FINGERPRINT, false) && 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.startAuth();
|
|
||||||
} 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(Activity 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,13 @@ 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.MagiskManager;
|
import com.topjohnwu.magisk.utils.SuConnector;
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.magisk.container.Policy;
|
|
||||||
import com.topjohnwu.magisk.container.SuLogEntry;
|
|
||||||
import com.topjohnwu.magisk.utils.Const;
|
|
||||||
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;
|
SuConnector.handleLogs(intent, 1);
|
||||||
String command, action;
|
|
||||||
Policy policy;
|
|
||||||
|
|
||||||
MagiskManager mm = Utils.getMagiskManager(context);
|
|
||||||
|
|
||||||
if (intent == null) return;
|
|
||||||
|
|
||||||
mode = intent.getIntExtra("mode", -1);
|
|
||||||
if (mode < 0) return;
|
|
||||||
|
|
||||||
if (mode == Const.Value.NOTIFY_USER_TO_OWNER) {
|
|
||||||
MagiskManager.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, context.getPackageManager());
|
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SuLogEntry log = new SuLogEntry(policy);
|
|
||||||
|
|
||||||
String message;
|
|
||||||
switch (action) {
|
|
||||||
case "allow":
|
|
||||||
message = context.getString(R.string.su_allow_toast, policy.appName);
|
|
||||||
log.action = true;
|
|
||||||
break;
|
|
||||||
case "deny":
|
|
||||||
message = context.getString(R.string.su_deny_toast, policy.appName);
|
|
||||||
log.action = false;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (policy.notification && mm.suNotificationType == Const.Value.NOTIFICATION_TOAST) {
|
|
||||||
MagiskManager.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
|
||||||
|
|||||||
@@ -1,15 +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.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;
|
||||||
|
|
||||||
@@ -27,16 +38,65 @@ public abstract class FingerprintHelper {
|
|||||||
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;
|
||||||
MagiskManager mm = MagiskManager.get();
|
MagiskManager mm = Data.MM();
|
||||||
KeyguardManager km = mm.getSystemService(KeyguardManager.class);
|
KeyguardManager km = mm.getSystemService(KeyguardManager.class);
|
||||||
FingerprintManager fm = mm.getSystemService(FingerprintManager.class);
|
FingerprintManager fm = mm.getSystemService(FingerprintManager.class);
|
||||||
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 {
|
||||||
MagiskManager mm = MagiskManager.get();
|
|
||||||
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
|
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
|
||||||
manager = mm.getSystemService(FingerprintManager.class);
|
manager = Data.MM().getSystemService(FingerprintManager.class);
|
||||||
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
|
cipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
|
||||||
+ KeyProperties.BLOCK_MODE_CBC + "/"
|
+ KeyProperties.BLOCK_MODE_CBC + "/"
|
||||||
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
|
+ KeyProperties.ENCRYPTION_PADDING_PKCS7);
|
||||||
@@ -62,30 +122,10 @@ public abstract class FingerprintHelper {
|
|||||||
|
|
||||||
public abstract void onAuthenticationFailed();
|
public abstract void onAuthenticationFailed();
|
||||||
|
|
||||||
public void startAuth() {
|
public void authenticate() {
|
||||||
cancel = new CancellationSignal();
|
cancel = new CancellationSignal();
|
||||||
FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
|
FingerprintManager.CryptoObject cryptoObject = new FingerprintManager.CryptoObject(cipher);
|
||||||
manager.authenticate(cryptoObject, cancel, 0, new FingerprintManager.AuthenticationCallback() {
|
manager.authenticate(cryptoObject, cancel, 0, new Callback(), null);
|
||||||
@Override
|
|
||||||
public void onAuthenticationError(int errorCode, CharSequence errString) {
|
|
||||||
FingerprintHelper.this.onAuthenticationError(errorCode, errString);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
|
|
||||||
FingerprintHelper.this.onAuthenticationHelp(helpCode, helpString);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
|
|
||||||
FingerprintHelper.this.onAuthenticationSucceeded(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onAuthenticationFailed() {
|
|
||||||
FingerprintHelper.this.onAuthenticationFailed();
|
|
||||||
}
|
|
||||||
}, null);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void cancel() {
|
public void cancel() {
|
||||||
@@ -109,4 +149,26 @@ public abstract class FingerprintHelper {
|
|||||||
keygen.init(builder.build());
|
keygen.init(builder.build());
|
||||||
return keygen.generateKey();
|
return keygen.generateKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class Callback extends FingerprintManager.AuthenticationCallback {
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationError(int errorCode, CharSequence errString) {
|
||||||
|
FingerprintHelper.this.onAuthenticationError(errorCode, errString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
|
||||||
|
FingerprintHelper.this.onAuthenticationHelp(helpCode, helpString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
|
||||||
|
FingerprintHelper.this.onAuthenticationSucceeded(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationFailed() {
|
||||||
|
FingerprintHelper.this.onAuthenticationFailed();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,18 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
import android.support.annotation.Keep;
|
|
||||||
|
|
||||||
public interface ISafetyNetHelper {
|
public interface ISafetyNetHelper {
|
||||||
|
|
||||||
int CAUSE_SERVICE_DISCONNECTED = 0x01;
|
int RESPONSE_ERR = 0x01;
|
||||||
int CAUSE_NETWORK_LOST = 0x02;
|
int CONNECTION_FAIL = 0x02;
|
||||||
int RESPONSE_ERR = 0x04;
|
|
||||||
int CONNECTION_FAIL = 0x08;
|
|
||||||
|
|
||||||
int BASIC_PASS = 0x10;
|
int BASIC_PASS = 0x10;
|
||||||
int CTS_PASS = 0x20;
|
int CTS_PASS = 0x20;
|
||||||
|
|
||||||
void attest();
|
void attest();
|
||||||
|
|
||||||
int getVersion();
|
int getVersion();
|
||||||
|
|
||||||
interface Callback {
|
interface Callback {
|
||||||
@Keep
|
|
||||||
void onResponse(int responseCode);
|
void onResponse(int responseCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.os.AsyncTask;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.MagiskManager;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
|
public class LocaleManager {
|
||||||
|
public static Locale locale = Locale.getDefault();
|
||||||
|
public final static Locale defaultLocale = Locale.getDefault();
|
||||||
|
public static List<Locale> locales;
|
||||||
|
|
||||||
|
public static void setLocale(MagiskManager mm) {
|
||||||
|
String localeConfig = mm.prefs.getString(Const.Key.LOCALE, "");
|
||||||
|
if (localeConfig.isEmpty()) {
|
||||||
|
locale = defaultLocale;
|
||||||
|
} else {
|
||||||
|
locale = Locale.forLanguageTag(localeConfig);
|
||||||
|
}
|
||||||
|
Locale.setDefault(locale);
|
||||||
|
Resources res = mm.getResources();
|
||||||
|
Configuration config = res.getConfiguration();
|
||||||
|
config.setLocale(locale);
|
||||||
|
res.updateConfiguration(config, res.getDisplayMetrics());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getString(Locale locale, @StringRes int id) {
|
||||||
|
Configuration config = new Configuration();
|
||||||
|
config.setLocale(locale);
|
||||||
|
return Data.MM().createConfigurationContext(config).getString(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadAvailableLocales() {
|
||||||
|
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||||
|
locales = new ArrayList<>();
|
||||||
|
HashSet<String> set = new HashSet<>();
|
||||||
|
Resources res = Data.MM().getResources();
|
||||||
|
Locale locale;
|
||||||
|
|
||||||
|
@StringRes int compareId = R.string.download_file_error;
|
||||||
|
|
||||||
|
// Add default locale
|
||||||
|
locales.add(Locale.ENGLISH);
|
||||||
|
set.add(getString(Locale.ENGLISH, compareId));
|
||||||
|
|
||||||
|
// Add some special locales
|
||||||
|
locales.add(Locale.TAIWAN);
|
||||||
|
set.add(getString(Locale.TAIWAN, compareId));
|
||||||
|
locale = new Locale("pt", "BR");
|
||||||
|
locales.add(locale);
|
||||||
|
set.add(getString(locale, compareId));
|
||||||
|
|
||||||
|
// Other locales
|
||||||
|
for (String s : res.getAssets().getLocales()) {
|
||||||
|
locale = Locale.forLanguageTag(s);
|
||||||
|
if (set.add(getString(locale, compareId))) {
|
||||||
|
locales.add(locale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Collections.sort(locales, (a, b) -> a.getDisplayName(a).compareTo(b.getDisplayName(b)));
|
||||||
|
Topic.publish(Topic.LOCALE_FETCH_DONE);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,7 @@ package com.topjohnwu.magisk.utils;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.BuildConfig;
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
import java.util.Locale;
|
|
||||||
|
|
||||||
public class Logger {
|
public class Logger {
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,87 @@
|
|||||||
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
|
import android.app.NotificationManager;
|
||||||
|
import android.app.PendingIntent;
|
||||||
|
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.R;
|
||||||
|
import com.topjohnwu.magisk.SplashActivity;
|
||||||
|
import com.topjohnwu.magisk.receivers.ManagerUpdate;
|
||||||
|
import com.topjohnwu.magisk.receivers.RebootReceiver;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
import androidx.core.app.TaskStackBuilder;
|
||||||
|
|
||||||
|
public class NotificationMgr {
|
||||||
|
|
||||||
|
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.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, Data.classMap.get(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, Data.classMap.get(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,77 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
|
||||||
|
|
||||||
import com.topjohnwu.utils.JarMap;
|
|
||||||
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.jar.JarEntry;
|
|
||||||
|
|
||||||
public class PatchAPK {
|
|
||||||
|
|
||||||
private static int findOffset(byte buf[], byte pattern[]) {
|
|
||||||
int offset = -1;
|
|
||||||
for (int i = 0; i < buf.length - pattern.length; ++i) {
|
|
||||||
boolean match = true;
|
|
||||||
for (int j = 0; j < pattern.length; ++j) {
|
|
||||||
if (buf[i + j] != pattern[j]) {
|
|
||||||
match = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (match) {
|
|
||||||
offset = i;
|
|
||||||
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)
|
|
||||||
return false;
|
|
||||||
byte[] dest = new byte[target.length - 2];
|
|
||||||
for (int i = 0; i < to.length(); ++i) {
|
|
||||||
dest[i * 2] = (byte) to.charAt(i);
|
|
||||||
}
|
|
||||||
System.arraycopy(dest, 0, xml, offset, dest.length);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean findAndPatch(byte xml[], String from, String to) {
|
|
||||||
byte target[] = (from + '\0').getBytes();
|
|
||||||
int offset = findOffset(xml, target);
|
|
||||||
if (offset < 0)
|
|
||||||
return fallbackPatch(xml, from, to);
|
|
||||||
System.arraycopy(to.getBytes(), 0, xml, offset, to.length());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean patchPackageID(String fileName, OutputStream out, String from, String to) {
|
|
||||||
try {
|
|
||||||
JarMap apk = new JarMap(fileName);
|
|
||||||
JarEntry je = apk.getJarEntry(Const.ANDROID_MANIFEST);
|
|
||||||
byte xml[] = apk.getRawData(je);
|
|
||||||
|
|
||||||
if (!findAndPatch(xml, from, to))
|
|
||||||
return false;
|
|
||||||
if (!findAndPatch(xml, from + ".provider", to + ".provider"))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Write in changes
|
|
||||||
apk.getOutputStream(je).write(xml);
|
|
||||||
|
|
||||||
// Sign the APK
|
|
||||||
ZipUtils.signZip(apk, out);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,38 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
package com.topjohnwu.magisk.utils;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.Const;
|
||||||
|
import com.topjohnwu.magisk.Data;
|
||||||
|
import com.topjohnwu.magisk.R;
|
||||||
|
import com.topjohnwu.superuser.BusyBox;
|
||||||
import com.topjohnwu.superuser.Shell;
|
import com.topjohnwu.superuser.Shell;
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
import com.topjohnwu.superuser.ShellUtils;
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
|
||||||
public class RootUtils {
|
import java.io.File;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
public class RootUtils extends Shell.Initializer {
|
||||||
|
|
||||||
|
static {
|
||||||
|
BusyBox.BB_PATH = new File(Const.BUSYBOX_PATH);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void uninstallPkg(String pkg) {
|
||||||
|
Shell.su("db_clean " + Const.USER_ID, "pm uninstall " + pkg).exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onInit(Context context, @NonNull Shell shell) {
|
||||||
|
Shell.Job job = shell.newJob();
|
||||||
|
if (shell.isRoot()) {
|
||||||
|
InputStream magiskUtils = context.getResources().openRawResource(R.raw.util_functions);
|
||||||
|
InputStream managerUtils = context.getResources().openRawResource(R.raw.utils);
|
||||||
|
job.add(magiskUtils).add(managerUtils);
|
||||||
|
|
||||||
public static void init() {
|
|
||||||
if (Shell.rootAccess()) {
|
|
||||||
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");
|
SuFile file = new SuFile("/sbin/.core/img");
|
||||||
if (file.exists()) {
|
if (file.exists()) {
|
||||||
@@ -19,20 +43,17 @@ public class RootUtils {
|
|||||||
Const.MAGISK_PATH = new SuFile("/magisk");
|
Const.MAGISK_PATH = new SuFile("/magisk");
|
||||||
}
|
}
|
||||||
Const.MAGISK_HOST_FILE = new SuFile(Const.MAGISK_PATH + "/.core/hosts");
|
Const.MAGISK_HOST_FILE = new SuFile(Const.MAGISK_PATH + "/.core/hosts");
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void uninstallPkg(String pkg) {
|
Data.loadMagiskInfo();
|
||||||
Shell.Sync.su("db_clean " + Const.USER_ID, "pm uninstall " + pkg);
|
} else {
|
||||||
}
|
InputStream nonroot = context.getResources().openRawResource(R.raw.nonroot_utils);
|
||||||
|
job.add(nonroot);
|
||||||
public static void patchDTBO() {
|
|
||||||
if (Shell.rootAccess()) {
|
|
||||||
MagiskManager mm = MagiskManager.get();
|
|
||||||
if (mm.magiskVersionCode >= Const.MAGISK_VER.DTBO_SUPPORT) {
|
|
||||||
if (Boolean.parseBoolean(ShellUtils.fastCmd("mm_patch_dtbo")))
|
|
||||||
ShowUI.dtboPatchedNotification();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
job.add("mount_partitions", "get_flags", "run_migrations", "export BOOTMODE=true").exec();
|
||||||
|
|
||||||
|
Data.keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
|
||||||
|
Data.keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT"));
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.superuser.BusyBox;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
public class ShellInitializer extends Shell.Initializer {
|
|
||||||
|
|
||||||
static {
|
|
||||||
BusyBox.BB_PATH = new File(Const.BUSYBOX_PATH);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onRootShellInit(Context context, @NonNull Shell shell) throws Exception {
|
|
||||||
try (InputStream magiskUtils = context.getResources().openRawResource(R.raw.util_functions);
|
|
||||||
InputStream managerUtils = context.getResources().openRawResource(R.raw.utils)
|
|
||||||
) {
|
|
||||||
shell.execTask((in, err, out) -> {
|
|
||||||
ShellUtils.pump(magiskUtils, in);
|
|
||||||
ShellUtils.pump(managerUtils, in);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
shell.run(null, null,
|
|
||||||
"mount_partitions",
|
|
||||||
"get_flags",
|
|
||||||
"run_migrations");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,273 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
|
||||||
|
|
||||||
import android.Manifest;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.NotificationManager;
|
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.support.v4.app.NotificationCompat;
|
|
||||||
import android.support.v4.app.TaskStackBuilder;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.FlashActivity;
|
|
||||||
import com.topjohnwu.magisk.MagiskManager;
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.magisk.SplashActivity;
|
|
||||||
import com.topjohnwu.magisk.asyncs.InstallMagisk;
|
|
||||||
import com.topjohnwu.magisk.asyncs.MarkDownWindow;
|
|
||||||
import com.topjohnwu.magisk.asyncs.RestoreImages;
|
|
||||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
|
||||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
|
||||||
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
|
||||||
import com.topjohnwu.magisk.receivers.ManagerUpdate;
|
|
||||||
import com.topjohnwu.magisk.receivers.RebootReceiver;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class ShowUI {
|
|
||||||
|
|
||||||
public static void magiskUpdateNotification() {
|
|
||||||
MagiskManager mm = MagiskManager.get();
|
|
||||||
|
|
||||||
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, mm.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 managerUpdateNotification() {
|
|
||||||
MagiskManager mm = MagiskManager.get();
|
|
||||||
String filename = Utils.fmt("MagiskManager-v%s(%d).apk",
|
|
||||||
mm.remoteManagerVersionString, mm.remoteManagerVersionCode);
|
|
||||||
|
|
||||||
Intent intent = new Intent(mm, ManagerUpdate.class);
|
|
||||||
intent.putExtra(Const.Key.INTENT_SET_LINK, mm.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 dtboPatchedNotification() {
|
|
||||||
MagiskManager mm = MagiskManager.get();
|
|
||||||
|
|
||||||
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());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void envFixDialog(Activity activity) {
|
|
||||||
MagiskManager mm = Utils.getMagiskManager(activity);
|
|
||||||
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
|
||||||
mm.remoteMagiskVersionString, mm.remoteMagiskVersionCode);
|
|
||||||
new AlertDialogBuilder(activity)
|
|
||||||
.setTitle(R.string.env_fix_title)
|
|
||||||
.setMessage(R.string.env_fix_msg)
|
|
||||||
.setCancelable(true)
|
|
||||||
.setPositiveButton(R.string.yes, (d, i) -> {
|
|
||||||
Utils.dlAndReceive(activity, new DownloadReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onDownloadDone(Context context, Uri uri) {
|
|
||||||
new InstallMagisk(activity, uri).exec();
|
|
||||||
}
|
|
||||||
}, mm.magiskLink, filename);
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.no_thanks, null)
|
|
||||||
.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void magiskInstallDialog(Activity activity) {
|
|
||||||
MagiskManager mm = Utils.getMagiskManager(activity);
|
|
||||||
String filename = Utils.fmt("Magisk-v%s(%d).zip",
|
|
||||||
mm.remoteMagiskVersionString, mm.remoteMagiskVersionCode);
|
|
||||||
AlertDialog.Builder b = new AlertDialogBuilder(activity)
|
|
||||||
.setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.magisk)))
|
|
||||||
.setMessage(mm.getString(R.string.repo_install_msg, filename))
|
|
||||||
.setCancelable(true)
|
|
||||||
.setPositiveButton(R.string.install, (d, i) -> {
|
|
||||||
List<String> options = new ArrayList<>();
|
|
||||||
options.add(mm.getString(R.string.download_zip_only));
|
|
||||||
options.add(mm.getString(R.string.patch_boot_file));
|
|
||||||
if (Shell.rootAccess()) {
|
|
||||||
options.add(mm.getString(R.string.direct_install));
|
|
||||||
}
|
|
||||||
new AlertDialog.Builder(activity)
|
|
||||||
.setTitle(R.string.select_method)
|
|
||||||
.setItems(
|
|
||||||
options.toArray(new String [0]),
|
|
||||||
(dialog, idx) -> {
|
|
||||||
DownloadReceiver receiver = null;
|
|
||||||
switch (idx) {
|
|
||||||
case 1:
|
|
||||||
if (mm.remoteMagiskVersionCode < 1400) {
|
|
||||||
MagiskManager.toast(R.string.no_boot_file_patch_support, Toast.LENGTH_LONG);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
MagiskManager.toast(R.string.boot_file_patch_msg, Toast.LENGTH_LONG);
|
|
||||||
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
|
|
||||||
intent.setType("*/*");
|
|
||||||
((com.topjohnwu.magisk.components.Activity) activity)
|
|
||||||
.startActivityForResult(intent, Const.ID.SELECT_BOOT,
|
|
||||||
(requestCode, resultCode, data) -> {
|
|
||||||
if (requestCode == Const.ID.SELECT_BOOT
|
|
||||||
&& resultCode == Activity.RESULT_OK && data != null) {
|
|
||||||
Utils.dlAndReceive(
|
|
||||||
activity,
|
|
||||||
new DownloadReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onDownloadDone(Context context, Uri uri) {
|
|
||||||
Intent intent = new Intent(mm, FlashActivity.class);
|
|
||||||
intent.setData(uri)
|
|
||||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
.putExtra(Const.Key.FLASH_SET_BOOT, data.getData())
|
|
||||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_BOOT);
|
|
||||||
mm.startActivity(intent);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mm.magiskLink,
|
|
||||||
filename
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
case 0:
|
|
||||||
receiver = new DownloadReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onDownloadDone(Context context, Uri uri) {
|
|
||||||
SnackbarMaker.showUri(activity, uri);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
receiver = new DownloadReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onDownloadDone(Context context, Uri uri) {
|
|
||||||
Intent intent = new Intent(mm, FlashActivity.class);
|
|
||||||
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION,
|
|
||||||
Const.Value.FLASH_MAGISK);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
receiver = new DownloadReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onDownloadDone(Context context, Uri uri) {
|
|
||||||
Intent intent = new Intent(mm, FlashActivity.class);
|
|
||||||
intent.setData(uri).putExtra(Const.Key.FLASH_ACTION,
|
|
||||||
Const.Value.FLASH_SECOND_SLOT);
|
|
||||||
activity.startActivity(intent);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
Utils.dlAndReceive(activity, receiver, mm.magiskLink, filename);
|
|
||||||
}
|
|
||||||
).show();
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.no_thanks, null);
|
|
||||||
if (!TextUtils.isEmpty(mm.magiskNoteLink)) {
|
|
||||||
b.setNeutralButton(R.string.release_notes, (d, i) -> {
|
|
||||||
if (mm.magiskNoteLink.contains("forum.xda-developers")) {
|
|
||||||
// Open forum links in browser
|
|
||||||
Intent openLink = new Intent(Intent.ACTION_VIEW, Uri.parse(mm.magiskNoteLink));
|
|
||||||
openLink.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
||||||
mm.startActivity(openLink);
|
|
||||||
} else {
|
|
||||||
new MarkDownWindow(activity, null, mm.magiskNoteLink).exec();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
b.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void managerInstallDialog(Activity activity) {
|
|
||||||
MagiskManager mm = Utils.getMagiskManager(activity);
|
|
||||||
String filename = Utils.fmt("MagiskManager-v%s(%d).apk",
|
|
||||||
mm.remoteManagerVersionString, mm.remoteManagerVersionCode);
|
|
||||||
AlertDialog.Builder b = new AlertDialogBuilder(activity)
|
|
||||||
.setTitle(mm.getString(R.string.repo_install_title, mm.getString(R.string.app_name)))
|
|
||||||
.setMessage(mm.getString(R.string.repo_install_msg, filename))
|
|
||||||
.setCancelable(true)
|
|
||||||
.setPositiveButton(R.string.install, (d, i) -> {
|
|
||||||
com.topjohnwu.magisk.components.Activity.runWithPermission(activity,
|
|
||||||
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
|
|
||||||
Intent intent = new Intent(mm, ManagerUpdate.class);
|
|
||||||
intent.putExtra(Const.Key.INTENT_SET_LINK, mm.managerLink);
|
|
||||||
intent.putExtra(Const.Key.INTENT_SET_FILENAME, filename);
|
|
||||||
mm.sendBroadcast(intent);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.setNegativeButton(R.string.no_thanks, null);
|
|
||||||
if (!TextUtils.isEmpty(mm.managerNoteLink)) {
|
|
||||||
b.setNeutralButton(R.string.app_changelog, (d, i) ->
|
|
||||||
new MarkDownWindow(activity, null, mm.managerNoteLink).exec());
|
|
||||||
}
|
|
||||||
b.show();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void uninstallDialog(Activity activity) {
|
|
||||||
MagiskManager mm = Utils.getMagiskManager(activity);
|
|
||||||
AlertDialog.Builder b = new AlertDialogBuilder(activity)
|
|
||||||
.setTitle(R.string.uninstall_magisk_title)
|
|
||||||
.setMessage(R.string.uninstall_magisk_msg)
|
|
||||||
.setNeutralButton(R.string.restore_img, (d, i) -> new RestoreImages(activity).exec());
|
|
||||||
if (!TextUtils.isEmpty(mm.uninstallerLink)) {
|
|
||||||
b.setPositiveButton(R.string.complete_uninstall, (d, i) ->
|
|
||||||
Utils.dlAndReceive(activity, new DownloadReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onDownloadDone(Context context, Uri uri) {
|
|
||||||
Intent intent = new Intent(context, FlashActivity.class)
|
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
||||||
.setData(uri)
|
|
||||||
.putExtra(Const.Key.FLASH_ACTION, Const.Value.UNINSTALL);
|
|
||||||
context.startActivity(intent);
|
|
||||||
}
|
|
||||||
}, mm.uninstallerLink, "magisk-uninstaller.zip"));
|
|
||||||
}
|
|
||||||
b.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user