Compare commits

...

86 Commits

Author SHA1 Message Date
topjohnwu
c2b016370b Make a copy of logcat and use that instead
When Magisk is magic mounting /system/bin, there is a chance that logcat would be temporarily unavailable. Leave a copy and use that for magisklogd
2018-10-20 21:46:12 -04:00
daveyannihilation
fc791b4371 Fix Dark theme to display cards as slightly lighter than background as per Material Design standards. Also redirect colors to app as opposed to calling on framework 2018-10-20 21:13:13 -04:00
topjohnwu
f76bb009f4 Update changelogs 2018-10-20 20:11:09 -04:00
topjohnwu
8a1292b295 Ask permissions to read internal storage 2018-10-20 19:42:46 -04:00
topjohnwu
d7d80d3fc1 Update encryption detection for determining default flags 2018-10-20 17:10:35 -04:00
topjohnwu
41b01003fd Always ACK before doing anything 2018-10-20 16:12:08 -04:00
topjohnwu
6557070ae1 Try to flush database before uninstalling 2018-10-20 15:31:41 -04:00
topjohnwu
e7e580e177 Remove support for Magisk lower than 1500 2018-10-20 15:04:15 -04:00
topjohnwu
dd9ddd2019 Remove unnecessary instruction from Defex hexpatch
Close #489
2018-10-20 00:28:09 -04:00
topjohnwu
74aae523ba Properly support boot image header v1
Close #695
2018-10-20 00:27:56 -04:00
topjohnwu
48c40f9516 Prevent Resources
Fix #619
2018-10-17 19:44:48 -04:00
topjohnwu
e0e7674715 Fix close button in FlashActivity 2018-10-17 15:36:09 -04:00
topjohnwu
e1a65276b9 Switch to general Samsung defex patch
Should be future proof unless code changes
2018-10-17 03:17:24 -04:00
muhammeteminturgut
469adc85ad Update Turkish translations 2018-10-16 21:11:43 -04:00
vvb2060
e1b181ca4e Hide system in MagiskHide list 2018-10-16 21:09:18 -04:00
topjohnwu
a4f0fbf8b7 Switch to butterknife
Finally support AndroidX and obfuscation
2018-10-16 21:00:01 -04:00
topjohnwu
190cdaddf8 Update README 2018-10-16 02:06:07 -04:00
topjohnwu
5c4ba13839 Add installation details 2018-10-16 01:55:28 -04:00
topjohnwu
e62630cf3e Add MagiskHide tutorials 2018-10-15 18:04:51 -04:00
topjohnwu
36fe7846c0 Update documentations
Still WIP
2018-10-15 04:33:42 -04:00
topjohnwu
8d150dd67a Update documentation
Still WIP
2018-10-15 00:46:37 -04:00
topjohnwu
506df00d81 Upgrade AGP 2018-10-12 21:51:58 -04:00
topjohnwu
a9121fa28f Reorganize libutils and cleanups 2018-10-12 21:46:09 -04:00
topjohnwu
d5a56d9e85 Fix bootloop for some devices with two /data
Close #654
2018-10-12 00:54:55 -04:00
topjohnwu
acf7c0c665 Minor reorganization of daemons 2018-10-12 00:50:47 -04:00
topjohnwu
619d48c97a Remove doc changelogs 2018-10-05 17:55:06 -04:00
topjohnwu
2cb198c38c Update README 2018-10-05 17:52:40 -04:00
topjohnwu
e8e39e0f3c Use poll instead of select
Close #637
2018-10-04 15:06:13 -04:00
topjohnwu
37860181d4 Finish su implementation 2018-10-04 14:41:48 -04:00
topjohnwu
d119dd9a0c Rewrite su daemon and client 2018-10-04 04:59:51 -04:00
topjohnwu
09ef19f7ec Code cleanups 2018-10-04 01:49:52 -04:00
topjohnwu
6a06c92fa6 Simplify su_info caches
No more lists. 99.999% it will only handle a single excessive requestor anyways.
2018-10-03 23:31:15 -04:00
topjohnwu
58ae596b0f Require fp auth when toggling su permission if required
Close #656
2018-09-29 02:21:14 -04:00
topjohnwu
f1ca21678d Set boolean when toggling 2018-09-29 02:02:41 -04:00
topjohnwu
d7eeef2c8a Separate fingerprint authentication dialog code 2018-09-29 01:57:51 -04:00
topjohnwu
4f626897f2 Cleanup 2018-09-29 00:28:12 -04:00
topjohnwu
b127e01845 Simplify debug flag propagation 2018-09-28 02:05:55 -04:00
topjohnwu
2118beeb23 Magisk-Modules-Repo now names repo with ID, simplify logic here 2018-09-28 01:58:28 -04:00
topjohnwu
5020cd1bbf Small cleanup 2018-09-28 01:25:43 -04:00
topjohnwu
cce636224c Reorganization 2018-09-27 18:26:41 -04:00
topjohnwu
60b3b8ddce Better incremental builds 2018-09-27 03:56:56 -04:00
topjohnwu
41446ec9ba Separate libutils and libsystemproperties 2018-09-27 03:30:16 -04:00
topjohnwu
df8b047bca Generalize logging interface 2018-09-27 03:11:10 -04:00
topjohnwu
12ced52012 Remove unused flag 2018-09-27 00:30:10 -04:00
topjohnwu
1d53335ae5 Dynamic load libselinux 2018-09-27 00:09:59 -04:00
topjohnwu
971a50d290 Update to Android Studio 3.2 2018-09-25 00:39:49 -04:00
topjohnwu
36dd9106a8 Stable AndroidX 2018-09-21 21:46:09 -04:00
John Wu
0a4ee3ffc7 Update README.MD 2018-09-21 12:01:59 -04:00
topjohnwu
cfe32f1a70 Update Magisk Manager changelogs 2018-09-20 22:34:09 -04:00
Taras
d877f5d5c6 update Ukrainian strings 2018-09-20 16:56:17 -04:00
yuchenlin
0ab6ffefb4 utils/misc.c: prevent file staying opened when function leaving
The utils function may be called in any situation, such as in daemon. We
should guarantee that all the resource got from this function released
normally.

Signed-off-by: yuchenlin <npes87184@gmail.com>
2018-09-20 16:55:48 -04:00
topjohnwu
a292a1d23a Cleanup and add new rules
Close #607
2018-09-20 16:55:16 -04:00
topjohnwu
3f87f6aee3 Fix output in Magisk Manager if no root exists 2018-09-20 16:21:22 -04:00
topjohnwu
04bcd145d3 Add a.a alias to BootSigner 2018-09-20 15:37:59 -04:00
topjohnwu
244e811291 Remove icon padding in preference screen
Courtesy of https://stackoverflow.com/a/51568782
2018-09-19 00:06:14 -04:00
topjohnwu
ac7467fb59 Optimize boot signing to use as little memory as possible 2018-09-18 23:48:21 -04:00
topjohnwu
2c0436216f Prevent null strings in modules/repos
Close #620, close #621
2018-09-18 10:04:12 -04:00
topjohnwu
017fbf267b Fix small theme config issue 2018-09-17 23:29:38 -04:00
topjohnwu
e6afbf2ec0 Force remove busybox from APK 2018-09-17 23:03:37 -04:00
topjohnwu
906b4aad9e New method of communication
Introduce a new communication method between Magisk and Magisk Manager.

Magisk used to hardcode classnames and send broadcast/start activities to
specific components. This new method makes no assumption of any class names,
so Magisk Manager can easily be fully obfuscated.

In addition, the new method connects Magisk and Magisk Manager with random
abstract Linux sockets instead of socket files in filesystems, bypassing
file system complexities (selinux, permissions and such)
2018-09-16 04:16:18 -04:00
topjohnwu
4cf8d41f6a Fix FlashActivity crash 2018-09-16 00:18:34 -04:00
topjohnwu
47c860142e Use ClassMaps to prevent errors 2018-09-16 00:08:13 -04:00
topjohnwu
2fba3f213b Use proper socket address length 2018-09-15 02:49:19 -04:00
topjohnwu
af7c6f9fce Fix FlashActivity crash 2018-09-15 01:45:10 -04:00
topjohnwu
78534deab6 Excessive obfuscation when building in release mode
Close #606
2018-09-14 23:00:39 -04:00
topjohnwu
6710314832 Allow all context to SIGCLD magisk
Fix #596
2018-09-11 11:04:51 -04:00
topjohnwu
0cd4fa6fa0 Simplify SignAPK code 2018-09-11 10:48:36 -04:00
topjohnwu
065949496e Migrate to AndroidX support library 2018-09-10 02:27:45 -04:00
topjohnwu
39c82576ae Prevent warning
Close #524
2018-09-09 12:36:42 -04:00
John Wu
37221a508d Update README.MD 2018-09-09 12:25:22 -04:00
John Wu
6b43a32a10 Delete repo_description.png 2018-09-09 10:41:02 -04:00
ImgBotApp
d7cd1ff142 [ImgBot] optimizes images
*Total -- 990.86kb -> 771.48kb (22.14%)

/docs/images/flashfire.png -- 223.55kb -> 153.39kb (31.39%)
/docs/images/repo_description.png -- 38.55kb -> 27.31kb (29.15%)
/docs/images/manager_reboot.png -- 147.43kb -> 111.50kb (24.37%)
/docs/images/install_inactive_slot.png -- 123.15kb -> 99.79kb (18.97%)
/docs/images/restore_img.png -- 134.17kb -> 109.22kb (18.59%)
/docs/images/disable_auto_ota.png -- 160.04kb -> 132.05kb (17.49%)
/docs/images/ota_done.png -- 163.97kb -> 138.22kb (15.71%)
2018-09-09 10:40:40 -04:00
Sandro Jäckel
659d947863 Updated German translations 2018-09-09 10:40:33 -04:00
Rom
39be7a6288 Update stub French translation 2018-09-09 00:38:47 -04:00
Rom
8ac976c579 Update French translation (#576) 2018-09-09 00:38:33 -04:00
Vladimír Kubala
70fd432c57 Update Slovak translation 2018-09-09 00:38:25 -04:00
Jonas Schubert
00135f2f49 updated full\res\values.de\strings.xml 2018-09-09 00:38:11 -04:00
Albert I
9b944bc29c Update Indonesian translations
Signed-off-by: Albert I <krascgq@outlook.co.id>
2018-09-09 00:38:02 -04:00
topjohnwu
d520b3d2a0 Request storage permission when patching boot images 2018-09-08 23:27:19 -04:00
topjohnwu
6f41d9855b Randomize service names
Fix Aniplex Game detections.
Close #502, close #513
2018-09-08 23:17:00 -04:00
topjohnwu
2d7c1da741 Better support for external config file 2018-09-06 14:25:35 -04:00
topjohnwu
c0f45b6b1e Add resetprop magic 2018-09-06 02:57:02 -04:00
topjohnwu
7a0025673c Use libsystemproperties in resetprop
Upstream to latest Android Pie
2018-09-06 02:57:02 -04:00
topjohnwu
ad7ec79903 Support custom config paths 2018-09-05 14:24:28 -04:00
topjohnwu
0543239cca Do not merge binaries if fake symlink 2018-09-01 11:15:05 -04:00
topjohnwu
ff3dad2457 Prevent upgrading database before upgrading to v17.0 2018-09-01 10:46:13 -04:00
233 changed files with 7425 additions and 5645 deletions

View File

@@ -1,7 +1,15 @@
# 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
1. Python 3.5+: run `build.py` script
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
@@ -9,20 +17,18 @@
5. (Windows Only) Python package Colorama: Install with `pip install colorama`, used for ANSI color codes
## Building Notes and Instructions
1. Building is supported on macOS, Linux, and Windows using the custom NDK: [FrankeNDK](https://github.com/topjohnwu/FrankeNDK).
2. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
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`
4. By default, `build.py` build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (via the `-r, --release` flag), you need a Java Keystore file `release-key.jks` (only `JKS` format is supported) to sign APKs and zips. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
## Documentation
[Link to Documentation](docs/README.MD)
1. Clone sources with submodules: `git clone --recurse-submodules https://github.com/topjohnwu/Magisk.git`
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. Set configurations in `config.prop`. A sample file `config.prop.sample` is provided as an example.
4. Run `build.py` with argument `-h` to see the built-in help message. The `-h` option also works for each supported actions, e.g. `./build.py binary -h`
5. By default, `build.py` build binaries and Magisk Manager in debug mode. If you want to build Magisk Manager in release mode (via the `-r, --release` flag), you need a Java Keystore file `release-key.jks` (only `JKS` format is supported) to sign APKs and zips. For more information, check out [Google's Official Documentation](https://developer.android.com/studio/publish/app-signing.html#signing-manually).
## License
```
Magisk, including all git submodules are free software:
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation,
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software Foundation,
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,

View File

@@ -1,7 +1,8 @@
apply plugin: 'com.android.application'
def configProps = new Properties()
configProps.load(new FileInputStream(rootProject.file('config.prop')))
def configPath = project.hasProperty('configPath') ? project.configPath : rootProject.file('config.prop')
configProps.load(new FileInputStream(configPath))
android {
compileSdkVersion rootProject.ext.compileSdkVersion
@@ -70,14 +71,20 @@ android {
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
fullImplementation project(':utils')
implementation "com.android.support:support-core-utils:${rootProject.ext.supportLibVersion}"
fullImplementation "com.android.support:preference-v7:${rootProject.ext.supportLibVersion}"
fullImplementation "com.android.support:recyclerview-v7:${rootProject.ext.supportLibVersion}"
fullImplementation "com.android.support:cardview-v7:${rootProject.ext.supportLibVersion}"
fullImplementation "com.android.support:design:${rootProject.ext.supportLibVersion}"
fullImplementation 'com.github.topjohnwu:libsu:2.0.1'
implementation "androidx.core:core:${rootProject.ext.androidXVersion}"
fullImplementation "androidx.preference:preference:${rootProject.ext.androidXVersion}"
fullImplementation "androidx.recyclerview:recyclerview:${rootProject.ext.androidXVersion}"
fullImplementation "androidx.cardview:cardview:${rootProject.ext.androidXVersion}"
fullImplementation "com.google.android.material:material:${rootProject.ext.androidXVersion}"
fullImplementation 'com.github.topjohnwu:libsu:2.0.2'
fullImplementation 'com.atlassian.commonmark:commonmark:0.11.0'
fullImplementation 'org.kamranzafar:jtar:2.3'
fullImplementation 'com.jakewharton:butterknife:8.8.1'
fullAnnotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
def butterKnifeVersion = '9.0.0-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}"
}
}

View File

@@ -16,19 +16,20 @@
# public *;
#}
# Don't obfuscate, we are open source anyway :)
-dontobfuscate
# BouncyCastle
-keep class org.bouncycastle.jcajce.provider.asymmetric.rsa.**SHA1** { *; }
-keep class org.bouncycastle.jcajce.provider.asymmetric.RSA** { *; }
-keep class org.bouncycastle.jcajce.provider.digest.SHA1** { *; }
-dontwarn javax.naming.**
# Gson
-keepattributes Signature
# Snet extention
-keepclassmembers class com.topjohnwu.magisk.utils.ISafetyNetHelper { *; }
# Strip logging
-assumenosideeffects class com.topjohnwu.magisk.utils.Logger {
public *** debug(...);
}
# Excessive obfuscation
-repackageclasses 'a'
-allowaccessmodification

View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.topjohnwu.magisk">
<uses-permission android:name="android.permission.VIBRATE" />
@@ -8,15 +9,18 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
android:name=".MagiskManager"
android:theme="@style/AppTheme">
android:name="a.q"
android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<!-- Activities -->
<activity
android:name=".MainActivity"
android:name="a.b"
android:configChanges="orientation|screenSize"
android:exported="true" />
<activity
android:name=".SplashActivity"
android:name="a.c"
android:configChanges="orientation|screenSize"
android:exported="true"
android:theme="@style/SplashTheme">
@@ -26,53 +30,68 @@
</intent-filter>
</activity>
<activity
android:name=".AboutActivity"
android:name="a.d"
android:theme="@style/AppTheme.StatusBar" />
<activity
android:name=".DonationActivity"
android:name="a.e"
android:theme="@style/AppTheme.StatusBar"/>
<activity
android:name=".FlashActivity"
android:name="a.f"
android:configChanges="keyboardHidden|orientation|screenSize"
android:screenOrientation="nosensor"
android:theme="@style/AppTheme.StatusBar" />
<activity
android:name=".NoUIActivity"
android:name="a.g"
android:theme="@style/AppTheme.Translucent" />
<!-- Superuser -->
<activity
android:name=".superuser.RequestActivity"
android:name="a.p"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity="internal.superuser"
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=".receivers.BootReceiver">
<!-- Receiver -->
<receiver android:name="a.h">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".receivers.PackageReceiver">
<receiver android:name="a.i">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_FULLY_REMOVED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
<receiver android:name=".receivers.ManagerUpdate" />
<receiver android:name=".receivers.RebootReceiver" />
<receiver android:name=".receivers.ShortcutReceiver">
<receiver android:name="a.j" />
<receiver android:name="a.k" />
<receiver android:name="a.l">
<intent-filter>
<action android:name="android.intent.action.LOCALE_CHANGED" />
</intent-filter>
</receiver>
<!-- Service -->
<service
android:name=".services.OnBootService"
android:name="a.m"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
<service
android:name=".services.UpdateCheckService"
android:name="a.n"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />

View File

@@ -0,0 +1,10 @@
package a;
import com.topjohnwu.magisk.utils.BootSigner;
import androidx.annotation.Keep;
@Keep
public class a extends BootSigner {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.MainActivity;
public class b extends MainActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.SplashActivity;
public class c extends SplashActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.AboutActivity;
public class d extends AboutActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.DonationActivity;
public class e extends DonationActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.FlashActivity;
public class f extends FlashActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.receivers.BootReceiver;
public class h extends BootReceiver {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.receivers.PackageReceiver;
public class i extends PackageReceiver {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.receivers.ManagerUpdate;
public class j extends ManagerUpdate {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.receivers.RebootReceiver;
public class k extends RebootReceiver {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.receivers.ShortcutReceiver;
public class l extends ShortcutReceiver {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.services.OnBootService;
public class m extends OnBootService {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.services.UpdateCheckService;
public class n extends UpdateCheckService {
/* stub */
}

View 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);
}
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.SuRequestActivity;
public class p extends SuRequestActivity {
/* stub */
}

View File

@@ -0,0 +1,7 @@
package a;
import com.topjohnwu.magisk.MagiskManager;
public class q extends MagiskManager {
/* stub */
}

View File

@@ -2,9 +2,6 @@ package com.topjohnwu.magisk;
import android.net.Uri;
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.view.View;
@@ -15,8 +12,10 @@ import com.topjohnwu.magisk.utils.Utils;
import java.util.Locale;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import butterknife.ButterKnife;
public class AboutActivity extends BaseActivity {
@@ -37,7 +36,7 @@ public class AboutActivity extends BaseActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
ButterKnife.bind(this);
new AboutActivity_ViewBinding(this);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(view -> finish());

View File

@@ -41,7 +41,6 @@ public class Const {
/* A list of apps that should not be shown as hide-able */
public static final List<String> HIDE_BLACKLIST = Arrays.asList(
"android",
Data.MM().getPackageName(),
"com.google.android.gms"
);
@@ -49,14 +48,10 @@ public class Const {
public static final int USER_ID = Process.myUid() / 100000;
public static final class MAGISK_VER {
public static final int UNIFIED = 1300;
public static final int FBE_AWARE = 1410;
public static final int RESETPROP_PERSIST = 1436;
public static final int MANAGER_HIDE = 1440;
public static final int HIDDEN_PATH = 1460;
public static final int REMOVE_LEGACY_LINK = 1630;
public static final int SEPOL_REFACTOR = 1640;
public static final int FIX_ENV = 1650;
public static final int DBVER_SIX = 17000;
}
public static class ID {
@@ -140,9 +135,6 @@ public class Const {
public static final int NAMESPACE_MODE_ISOLATE = 2;
public static final int NO_NOTIFICATION = 0;
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_AUTO_DENY = 1;
public static final int SU_AUTO_ALLOW = 2;

View File

@@ -5,6 +5,14 @@ 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;
@@ -18,11 +26,14 @@ 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;
@@ -60,12 +71,31 @@ public class Data {
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((magiskVersionCode >= Const.MAGISK_VER.RESETPROP_PERSIST ?
"resetprop -p " : "getprop ") + Const.MAGISKHIDE_PROP);
String s = ShellUtils.fastCmd(("resetprop -p ") + Const.MAGISKHIDE_PROP);
magiskHide = s.isEmpty() || Integer.parseInt(s) != 0;
} catch (NumberFormatException ignored) {}
}

View File

@@ -2,16 +2,15 @@ package com.topjohnwu.magisk;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.Nullable;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import butterknife.ButterKnife;
public class DonationActivity extends BaseActivity {
@@ -28,7 +27,7 @@ public class DonationActivity extends BaseActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_donation);
ButterKnife.bind(this);
new DonationActivity_ViewBinding(this);
setSupportActionBar(toolbar);
toolbar.setNavigationOnClickListener(view -> finish());

View File

@@ -4,8 +4,6 @@ import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.view.View;
import android.widget.Button;
@@ -31,8 +29,9 @@ import java.util.Calendar;
import java.util.List;
import java.util.Locale;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.widget.Toolbar;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
public class FlashActivity extends BaseActivity {
@@ -45,11 +44,6 @@ public class FlashActivity extends BaseActivity {
private List<String> logs;
@OnClick(R.id.no_thanks)
void dismiss() {
finish();
}
@OnClick(R.id.reboot)
void reboot() {
Shell.su("/system/bin/reboot").submit();
@@ -88,7 +82,8 @@ public class FlashActivity extends BaseActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_flash);
ButterKnife.bind(this);
new FlashActivity_ViewBinding(this);
setSupportActionBar(toolbar);
ActionBar ab = getSupportActionBar();
if (ab != null) {
@@ -145,6 +140,12 @@ public class FlashActivity extends BaseActivity {
}
}
@OnClick(R.id.close)
@Override
public void finish() {
super.finish();
}
@Override
public void onBackPressed() {
// Prevent user accidentally press back button

View File

@@ -38,7 +38,7 @@ public class MagiskManager extends ContainerApp {
Shell.Config.setInitializer(RootUtils.class);
prefs = PreferenceManager.getDefaultSharedPreferences(this);
mDB = MagiskDatabaseHelper.getInstance(this);
mDB = MagiskDatabaseHelper.getInstance();
String pkg = mDB.getStrings(Const.Key.SU_MANAGER, null);
if (pkg != null && getPackageName().equals(Const.ORIG_PKG_NAME)) {

View File

@@ -3,17 +3,11 @@ package com.topjohnwu.magisk;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.DrawerLayout;
import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import com.google.android.material.navigation.NavigationView;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.fragments.LogFragment;
import com.topjohnwu.magisk.fragments.MagiskFragment;
@@ -26,8 +20,13 @@ import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.Topic;
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.ButterKnife;
public class MainActivity extends BaseActivity
implements NavigationView.OnNavigationItemSelectedListener, Topic.Subscriber {
@@ -36,9 +35,9 @@ public class MainActivity extends BaseActivity
private int mDrawerItem;
private static boolean fromShortcut = false;
@BindView(R.id.drawer_layout) DrawerLayout drawer;
@BindView(R.id.toolbar) public Toolbar toolbar;
@BindView(R.id.nav_view) public NavigationView navigationView;
@BindView(R.id.drawer_layout) DrawerLayout drawer;
@BindView(R.id.nav_view) NavigationView navigationView;
private float toolbarElevation;
@@ -50,13 +49,13 @@ public class MainActivity extends BaseActivity
@Override
protected void onCreate(final Bundle savedInstanceState) {
if (!mm.hasInit) {
startActivity(new Intent(this, SplashActivity.class));
startActivity(new Intent(this, Data.classMap.get(SplashActivity.class)));
finish();
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
new MainActivity_ViewBinding(this);
setSupportActionBar(toolbar);
@@ -112,11 +111,6 @@ public class MainActivity extends BaseActivity
return true;
}
@Override
public int[] getSubscribedTopics() {
return new int[] {Topic.RELOAD_ACTIVITY};
}
@Override
public void onPublish(int topic, Object[] result) {
recreate();
@@ -125,7 +119,6 @@ public class MainActivity extends BaseActivity
public void checkHideSection() {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.magiskhide).setVisible(Shell.rootAccess() &&
Data.magiskVersionCode >= Const.MAGISK_VER.UNIFIED &&
mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false));
menu.findItem(R.id.modules).setVisible(Shell.rootAccess() && Data.magiskVersionCode >= 0);
menu.findItem(R.id.downloads).setVisible(Download.checkNetworkStatus(this)
@@ -196,11 +189,11 @@ public class MainActivity extends BaseActivity
displayFragment(new SettingsFragment(), true);
break;
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, DonationActivity.class));
startActivity(new Intent(this, Data.classMap.get(DonationActivity.class)));
mDrawerItem = bak;
break;
}

View File

@@ -1,9 +1,9 @@
package com.topjohnwu.magisk;
import android.support.annotation.NonNull;
import com.topjohnwu.magisk.components.BaseActivity;
import androidx.annotation.NonNull;
public class NoUIActivity extends BaseActivity {
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {

View File

@@ -44,7 +44,7 @@ public class SplashActivity extends BaseActivity {
}
// Setup shortcuts
sendBroadcast(new Intent(this, ShortcutReceiver.class));
sendBroadcast(new Intent(this, Data.classMap.get(ShortcutReceiver.class)));
if (Download.checkNetworkStatus(this)) {
// Fire update check
@@ -58,7 +58,7 @@ public class SplashActivity extends BaseActivity {
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(BaseActivity.INTENT_PERM, getIntent().getStringExtra(BaseActivity.INTENT_PERM));
startActivity(intent);

View 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();
}
}

View File

@@ -3,11 +3,7 @@ package com.topjohnwu.magisk.adapters;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -19,8 +15,8 @@ import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;
@@ -28,8 +24,9 @@ import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
@@ -53,25 +50,12 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
return new ViewHolder(v);
}
private String getLabel(ApplicationInfo info) {
if (info.labelRes > 0) {
try {
Resources res = pm.getResourcesForApplication(info);
Configuration config = new Configuration();
config.setLocale(LocaleManager.locale);
res.updateConfiguration(config, res.getDisplayMetrics());
return res.getString(info.labelRes);
} catch (PackageManager.NameNotFoundException ignored) { /* Impossible */ }
}
return info.loadLabel(pm).toString();
}
private void loadApps() {
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) {
if (Const.HIDE_BLACKLIST.contains(info.packageName) || !info.enabled || info.uid == 1000) {
i.remove();
}
}
@@ -79,7 +63,8 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
boolean ah = hideList.contains(a.packageName);
boolean bh = hideList.contains(b.packageName);
if (ah == bh) {
return getLabel(a).toLowerCase().compareTo(getLabel(b).toLowerCase());
return Utils.getAppLabel(a, pm).toLowerCase()
.compareTo(Utils.getAppLabel(b, pm).toLowerCase());
} else if (ah) {
return -1;
} else {
@@ -94,7 +79,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
ApplicationInfo info = showList.get(position);
holder.appIcon.setImageDrawable(info.loadIcon(pm));
holder.appName.setText(getLabel(info));
holder.appName.setText(Utils.getAppLabel(info, pm));
holder.appPackage.setText(info.packageName);
holder.checkBox.setOnCheckedChangeListener(null);
@@ -132,11 +117,11 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
ViewHolder(View 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) {
return !TextUtils.isEmpty(s) && s.toLowerCase().contains(filter);
@@ -150,7 +135,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
showList = new ArrayList<>();
String filter = constraint.toString().toLowerCase();
for (ApplicationInfo info : fullList) {
if (lowercaseContains(getLabel(info), filter)
if (lowercaseContains(Utils.getAppLabel(info, pm), filter)
|| lowercaseContains(info.packageName, filter)) {
showList.add(info);
}

View File

@@ -1,8 +1,6 @@
package com.topjohnwu.magisk.adapters;
import android.content.Context;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
@@ -11,6 +9,7 @@ import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Module;
@@ -18,8 +17,9 @@ import com.topjohnwu.superuser.Shell;
import java.util.List;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHolder> {
@@ -29,6 +29,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
mList = list;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false);
@@ -46,9 +47,9 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
String noInfo = context.getString(R.string.no_info_provided);
holder.title.setText(module.getName());
holder.versionName.setText( TextUtils.isEmpty(version) ? noInfo : version);
holder.author.setText( TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author));
holder.description.setText( TextUtils.isEmpty(description) ? noInfo : description);
holder.versionName.setText(TextUtils.isEmpty(version) ? noInfo : version);
holder.author.setText(TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author));
holder.description.setText(TextUtils.isEmpty(description) ? noInfo : description);
holder.checkBox.setOnCheckedChangeListener(null);
holder.checkBox.setChecked(module.isEnabled());
@@ -114,7 +115,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
new ModulesAdapter$ViewHolder_ViewBinding(this, itemView);
if (!Shell.rootAccess()) {
checkBox.setEnabled(false);

View File

@@ -2,8 +2,6 @@ package com.topjohnwu.magisk.adapters;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -11,19 +9,23 @@ import android.widget.ImageView;
import android.widget.Switch;
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.components.CustomAlertDialog;
import com.topjohnwu.magisk.components.ExpandableView;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.database.MagiskDatabaseHelper;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> {
@@ -38,6 +40,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
this.pm = pm;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
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.packageName.setText(policy.packageName);
holder.appIcon.setImageDrawable(policy.info.loadIcon(pm));
holder.masterSwitch.setOnCheckedChangeListener((v, isChecked) -> {
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);
holder.notificationSwitch.setOnCheckedChangeListener(null);
holder.loggingSwitch.setOnCheckedChangeListener(null);
holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW);
holder.notificationSwitch.setChecked(policy.notification);
holder.loggingSwitch.setChecked(policy.logging);
holder.masterSwitch.setOnClickListener(v -> {
boolean isChecked = holder.masterSwitch.isChecked();
Runnable r = () -> {
if ((isChecked && policy.policy == Policy.DENY) ||
(!isChecked && policy.policy == Policy.ALLOW)) {
policy.policy = isChecked ? Policy.ALLOW : Policy.DENY;
String message = v.getContext().getString(
isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName);
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.updatePolicy(policy);
}
};
if (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) -> {
@@ -107,9 +130,6 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
.setNegativeButton(R.string.no_thanks, null)
.setCancelable(true)
.show());
holder.masterSwitch.setChecked(policy.policy == Policy.ALLOW);
holder.notificationSwitch.setChecked(policy.notification);
holder.loggingSwitch.setChecked(policy.logging);
// Hide for now
holder.moreInfo.setVisibility(View.GONE);
@@ -137,7 +157,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
new PolicyAdapter$ViewHolder_ViewBinding(this, itemView);
container.expandLayout = expandLayout;
setupExpandable();
}

View File

@@ -2,7 +2,6 @@ package com.topjohnwu.magisk.adapters;
import android.content.Context;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.Pair;
import android.view.LayoutInflater;
@@ -25,8 +24,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, ReposAdapter.RepoHolder> {
@@ -89,11 +88,16 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
Repo repo = repoPairs.get(section).second.get(position);
Context context = holder.itemView.getContext();
holder.title.setText(repo.getName());
holder.versionName.setText(repo.getVersion());
String name = repo.getName();
String version = repo.getVersion();
String author = repo.getAuthor();
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
holder.description.setText(repo.getDescription());
String description = repo.getDescription();
String noInfo = context.getString(R.string.no_info_provided);
holder.title.setText(TextUtils.isEmpty(name) ? noInfo : name);
holder.versionName.setText(TextUtils.isEmpty(version) ? noInfo : version);
holder.author.setText(TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author));
holder.description.setText(TextUtils.isEmpty(description) ? noInfo : description);
holder.updateTime.setText(context.getString(R.string.updated_on, repo.getLastUpdateString()));
holder.infoLayout.setOnClickListener(v ->
@@ -165,7 +169,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
SectionHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
new ReposAdapter$SectionHolder_ViewBinding(this, itemView);
}
}
@@ -181,7 +185,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
RepoHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
new ReposAdapter$RepoHolder_ViewBinding(this, itemView);
}
}

View File

@@ -1,8 +1,9 @@
package com.topjohnwu.magisk.adapters;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
public abstract class SectionedAdapter<S extends RecyclerView.ViewHolder, C extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

View File

@@ -1,7 +1,6 @@
package com.topjohnwu.magisk.adapters;
import android.database.Cursor;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -20,8 +19,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, SuLogAdapter.LogViewHolder> {
@@ -124,7 +123,7 @@ public class SuLogAdapter extends SectionedAdapter<SuLogAdapter.SectionHolder, S
SectionHolder(View 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) {
super(itemView);
ButterKnife.bind(this, itemView);
new SuLogAdapter$LogViewHolder_ViewBinding(this, itemView);
container.expandLayout = expandLayout;
setupExpandable();
}

View File

@@ -1,13 +1,13 @@
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.List;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
public class TabFragmentAdapter extends FragmentPagerAdapter {
private List<Fragment> fragmentList;

View File

@@ -4,7 +4,6 @@ import android.app.Activity;
import android.app.ProgressDialog;
import android.net.Uri;
import android.os.Build;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.view.View;
import android.widget.Toast;
@@ -44,6 +43,8 @@ import java.net.HttpURLConnection;
import java.util.Arrays;
import java.util.List;
import androidx.annotation.NonNull;
public class InstallMagisk extends ParallelTask<Void, Void, Boolean> {
private static final int PATCH_MODE = 0;

View File

@@ -1,7 +1,6 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.support.v7.app.AlertDialog;
import android.webkit.WebView;
import com.topjohnwu.magisk.Data;
@@ -18,6 +17,8 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import androidx.appcompat.app.AlertDialog;
public class MarkDownWindow extends ParallelTask<Void, Void, String> {
private String mTitle;

View File

@@ -111,6 +111,7 @@ public class PatchAPK {
repack.delete();
mm.mDB.setStrings(Const.Key.SU_MANAGER, pkg);
mm.mDB.flush();
Data.exportPrefs();
RootUtils.uninstallPkg(Const.ORIG_PKG_NAME);

View File

@@ -4,7 +4,6 @@ import android.Manifest;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
@@ -35,6 +34,8 @@ import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.JarOutputStream;
import androidx.annotation.NonNull;
public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
private ProgressDialog progressDialog;
@@ -140,7 +141,7 @@ public class ProcessRepoZip extends ParallelTask<Void, Object, Boolean> {
if (result) {
Uri uri = Uri.fromFile(mFile);
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);
activity.startActivity(intent);
} else {

View File

@@ -66,17 +66,15 @@ public class UpdateRepos {
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject rawRepo = jsonArray.getJSONObject(i);
String id = rawRepo.getString("description");
String name = rawRepo.getString("name");
String id = rawRepo.getString("name");
Date date = dateFormat.parse(rawRepo.getString("pushed_at"));
Set<String> set = Collections.synchronizedSet(cached);
threadPool.execute(() -> {
Repo repo = mm.repoDB.getRepo(id);
try {
if (repo == null)
repo = new Repo(name);
repo = new Repo(id);
else
set.remove(id);
cached.remove(id);
repo.update(date);
mm.repoDB.addRepo(repo);
} catch (Repo.IllegalRepoException e) {
@@ -141,7 +139,7 @@ public class UpdateRepos {
public void exec(boolean force) {
Topic.reset(Topic.REPO_LOAD_DONE);
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
cached = mm.repoDB.getRepoIDSet();
cached = Collections.synchronizedSet(mm.repoDB.getRepoIDSet());
threadPool = Executors.newFixedThreadPool(CORE_POOL_SIZE);
if (loadPage(0)) {

View File

@@ -29,7 +29,6 @@ import android.widget.TextView;
import com.topjohnwu.magisk.R;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* @author dvdandroid
@@ -52,7 +51,7 @@ public class AboutCardRow extends LinearLayout {
public AboutCardRow(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
LayoutInflater.from(context).inflate(R.layout.info_item_row, this);
ButterKnife.bind(this, this);
new AboutCardRow_ViewBinding(this, this);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.AboutCardRow, 0, 0);
String title;
@@ -69,8 +68,6 @@ public class AboutCardRow extends LinearLayout {
@Override
public void setOnClickListener(OnClickListener l) {
super.setOnClickListener(l);
mView.setOnClickListener(l);
}

View File

@@ -1,15 +1,18 @@
package com.topjohnwu.magisk.components;
import android.content.Intent;
import android.support.v4.app.Fragment;
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();
@@ -27,6 +30,13 @@ public class BaseFragment extends Fragment implements Topic.AutoSubscriber {
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);

View File

@@ -2,11 +2,6 @@ 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;
@@ -15,8 +10,12 @@ 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;
import butterknife.ButterKnife;
public class CustomAlertDialog extends AlertDialog.Builder {
@@ -37,7 +36,7 @@ public class CustomAlertDialog extends AlertDialog.Builder {
@BindView(R.id.neutral) public Button neutral;
ViewHolder(View v) {
ButterKnife.bind(this, v);
new CustomAlertDialog$ViewHolder_ViewBinding(this, v);
messageView.setVisibility(View.GONE);
negative.setVisibility(View.GONE);
positive.setVisibility(View.GONE);

View File

@@ -1,11 +1,12 @@
package com.topjohnwu.magisk.components;
import android.app.Activity;
import android.support.annotation.NonNull;
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) {

View File

@@ -4,9 +4,6 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.StyleRes;
import android.support.v7.app.AppCompatActivity;
import android.view.WindowManager;
import com.topjohnwu.magisk.Data;
@@ -15,6 +12,10 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.LocaleManager;
import com.topjohnwu.magisk.utils.Topic;
import androidx.annotation.Nullable;
import androidx.annotation.StyleRes;
import androidx.appcompat.app.AppCompatActivity;
public abstract class FlavorActivity extends AppCompatActivity implements Topic.AutoSubscriber {
private ActivityResultListener activityResultListener;
@@ -42,11 +43,11 @@ public abstract class FlavorActivity extends AppCompatActivity implements Topic.
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Topic.subscribe(this);
if (Data.isDarkTheme && getDarkTheme() != -1) {
setTheme(getDarkTheme());
}
super.onCreate(savedInstanceState);
}
@Override

View File

@@ -1,10 +1,10 @@
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.support.design.widget.Snackbar;
import android.support.v7.app.AlertDialog;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
@@ -17,6 +17,8 @@ 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) {
@@ -26,23 +28,21 @@ class InstallMethodDialog extends AlertDialog.Builder {
Intent intent;
switch (idx) {
case 1:
if (Data.remoteMagiskVersionCode < 1400) {
SnackbarMaker.make(activity, R.string.no_boot_file_patch_support,
Snackbar.LENGTH_LONG).show();
return;
}
Utils.toast(R.string.boot_file_patch_msg, Toast.LENGTH_LONG);
intent = new Intent(Intent.ACTION_GET_CONTENT).setType("*/*");
activity.startActivityForResult(intent, Const.ID.SELECT_BOOT,
(requestCode, resultCode, data) -> {
if (requestCode == Const.ID.SELECT_BOOT &&
resultCode == BaseActivity.RESULT_OK && data != null) {
Intent i = new Intent(activity, FlashActivity.class)
.putExtra(Const.Key.FLASH_SET_BOOT, data.getData())
.putExtra(Const.Key.FLASH_ACTION, Const.Value.PATCH_BOOT);
activity.startActivity(i);
}
});
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",
@@ -55,22 +55,22 @@ class InstallMethodDialog extends AlertDialog.Builder {
}, Data.magiskLink, filename);
break;
case 2:
intent = new Intent(activity, FlashActivity.class)
intent = new Intent(activity, Data.classMap.get(FlashActivity.class))
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_MAGISK);
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, FlashActivity.class)
.putExtra(Const.Key.FLASH_ACTION, Const.Value.FLASH_INACTIVE_SLOT);
activity.startActivity(it);
})
.setNegativeButton(R.string.no_thanks, null)
.show();
.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:
}

View File

@@ -2,7 +2,6 @@ package com.topjohnwu.magisk.components;
import android.Manifest;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.topjohnwu.magisk.Const;
@@ -13,6 +12,8 @@ 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) {
@@ -25,7 +26,7 @@ public class ManagerInstallDialog extends CustomAlertDialog {
setCancelable(true);
setPositiveButton(R.string.install, (d, i) -> activity.runWithPermission(
new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
Intent intent = new Intent(mm, ManagerUpdate.class);
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);

View File

@@ -2,14 +2,15 @@ package com.topjohnwu.magisk.components;
import android.app.Activity;
import android.net.Uri;
import android.support.annotation.StringRes;
import android.support.design.widget.Snackbar;
import android.view.View;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.StringRes;
public class SnackbarMaker {
public static Snackbar make(Activity activity, CharSequence text, int duration) {
@@ -34,7 +35,7 @@ public class SnackbarMaker {
}
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);
}

View File

@@ -5,7 +5,6 @@ import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.widget.Toast;
@@ -19,6 +18,8 @@ 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) {
@@ -44,7 +45,7 @@ public class UninstallDialog extends CustomAlertDialog {
Download.receive(activity, new DownloadReceiver() {
@Override
public void onDownloadDone(Context context, Uri uri) {
Intent intent = new Intent(context, FlashActivity.class)
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);

View File

@@ -1,29 +1,35 @@
package com.topjohnwu.magisk.container;
import android.content.ContentValues;
import android.database.Cursor;
import android.support.annotation.NonNull;
import java.util.List;
import androidx.annotation.NonNull;
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;
protected BaseModule() {}
protected BaseModule() {
mId = mName = mVersion = mAuthor = mDescription = "";
}
protected BaseModule(Cursor c) {
mId = c.getString(c.getColumnIndex("id"));
mName = c.getString(c.getColumnIndex("name"));
mVersion = c.getString(c.getColumnIndex("version"));
mId = nonNull(c.getString(c.getColumnIndex("id")));
mName = nonNull(c.getString(c.getColumnIndex("name")));
mVersion = nonNull(c.getString(c.getColumnIndex("version")));
mVersionCode = c.getInt(c.getColumnIndex("versionCode"));
mAuthor = c.getString(c.getColumnIndex("author"));
mDescription = c.getString(c.getColumnIndex("description"));
mAuthor = nonNull(c.getString(c.getColumnIndex("author")));
mDescription = nonNull(c.getString(c.getColumnIndex("description")));
minMagiskVersion = c.getInt(c.getColumnIndex("minMagisk"));
}
private String nonNull(String s) {
return s == null ? "" : s;
}
public ContentValues getContentValues() {
ContentValues values = new ContentValues();
values.put("id", mId);

View File

@@ -4,7 +4,10 @@ import android.content.ContentValues;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.support.annotation.NonNull;
import com.topjohnwu.magisk.utils.Utils;
import androidx.annotation.NonNull;
public class Policy implements Comparable<Policy>{
@@ -25,7 +28,7 @@ public class Policy implements Comparable<Policy>{
this.uid = uid;
packageName = pkgs[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 {

View File

@@ -14,16 +14,14 @@ import java.util.Date;
public class Repo extends BaseModule {
private String repoName;
private Date mLastUpdate;
public Repo(String name) {
repoName = name;
public Repo(String id) {
setId(id);
}
public Repo(Cursor c) {
super(c);
repoName = c.getString(c.getColumnIndex("repo_name"));
mLastUpdate = new Date(c.getLong(c.getColumnIndex("last_update")));
}
@@ -32,17 +30,14 @@ public class Repo extends BaseModule {
try {
parseProps(props);
} 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) {
throw new IllegalRepoException("Repo [" + repoName + "] does not contain versionCode");
throw new IllegalRepoException("Repo [" + getId() + "] does not contain versionCode");
}
if (getMinMagiskVersion() < Const.MIN_MODULE_VER()) {
Logger.debug("Repo [" + repoName + "] is outdated");
Logger.debug("Repo [" + getId() + "] is outdated");
}
}
@@ -54,25 +49,20 @@ public class Repo extends BaseModule {
@Override
public ContentValues getContentValues() {
ContentValues values = super.getContentValues();
values.put("repo_name", repoName);
values.put("last_update", mLastUpdate.getTime());
return values;
}
public String getRepoName() {
return repoName;
}
public String getZipUrl() {
return String.format(Const.Url.ZIP_URL, repoName);
return String.format(Const.Url.ZIP_URL, getId());
}
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() {
return String.format(Const.Url.FILE_URL, repoName, "README.md");
return String.format(Const.Url.FILE_URL, getId(), "README.md");
}
public String getLastUpdateString() {

View File

@@ -1,7 +1,5 @@
package com.topjohnwu.magisk.container;
import android.support.annotation.NonNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -9,6 +7,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import androidx.annotation.NonNull;
public class ValueSortedMap<K, V extends Comparable<? super V>> extends HashMap<K, V> {
private List<V> sorted = new ArrayList<>();

View File

@@ -7,7 +7,6 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Process;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.widget.Toast;
@@ -29,86 +28,84 @@ import java.util.Collections;
import java.util.Date;
import java.util.List;
import androidx.annotation.NonNull;
public class MagiskDatabaseHelper {
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 LOG_TABLE = "logs";
private static final String SETTINGS_TABLE = "settings";
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 SQLiteDatabase db;
private MagiskManager mm;
@NonNull
public static MagiskDatabaseHelper getInstance(MagiskManager mm) {
public static MagiskDatabaseHelper getInstance() {
try {
return new MagiskDatabaseHelper(mm);
return new MagiskDatabaseHelper();
} catch (Exception e) {
// Let's cleanup everything and try again
Shell.su("db_clean '*'").exec();
return new MagiskDatabaseHelper(mm);
return new MagiskDatabaseHelper();
}
}
private MagiskDatabaseHelper(MagiskManager context) {
mm = context;
pm = mm.getPackageManager();
db = openDatabase(mm);
private MagiskDatabaseHelper() {
pm = Data.MM().getPackageManager();
init();
}
private void init() {
db = openDatabase();
db.disableWriteAheadLogging();
int version = db.getVersion();
if (version < DATABASE_VER) {
onUpgrade(db, version);
} else if (version > DATABASE_VER) {
int version = Data.magiskVersionCode >= Const.MAGISK_VER.DBVER_SIX ? DATABASE_VER : OLD_DATABASE_VER;
int curVersion = db.getVersion();
if (curVersion < version) {
onUpgrade(db, curVersion);
} else if (curVersion > DATABASE_VER) {
/* Higher than we can possibly support */
onDowngrade(db);
}
db.setVersion(DATABASE_VER);
db.setVersion(version);
clearOutdated();
}
private SQLiteDatabase openDatabase(MagiskManager mm) {
final File DB_FILE = new File(Utils.fmt("/sbin/.core/db-%d/magisk.db", Const.USER_ID));
private SQLiteDatabase openDatabase() {
MagiskManager mm = Data.MM();
Context de = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
? mm.createDeviceProtectedStorageContext() : mm;
if (!DB_FILE.canWrite()) {
if (!MANAGER_DB.canWrite()) {
if (!Shell.rootAccess()) {
// We don't want the app to crash, create a db and return
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
}
// Cleanup
Shell.su("db_clean " + Const.USER_ID).exec();
if (Data.magiskVersionCode < Const.MAGISK_VER.FBE_AWARE) {
// Super old legacy mode
return mm.openOrCreateDatabase("su.db", Context.MODE_PRIVATE, null);
} else if (Data.magiskVersionCode < Const.MAGISK_VER.HIDDEN_PATH) {
// Legacy mode with FBE aware
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
de.moveDatabaseFrom(mm, "su.db");
}
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 (Data.magiskVersionCode < Const.MAGISK_VER.SEPOL_REFACTOR) {
// We need some additional policies on old versions
Shell.su("db_sepatch").exec();
}
if (!GLOBAL_DB.exists()) {
Shell.su("db_init").exec();
SQLiteDatabase.openOrCreateDatabase(GLOBAL_DB, null).close();
Shell.su("db_restore").exec();
}
// Global database
final SuFile GLOBAL_DB = new SuFile("/data/adb/magisk.db");
mm.deleteDatabase("su.db");
de.deleteDatabase("su.db");
if (Data.magiskVersionCode < Const.MAGISK_VER.SEPOL_REFACTOR) {
// We need some additional policies on old versions
Shell.su("db_sepatch").exec();
}
if (!GLOBAL_DB.exists()) {
Shell.su("db_init").exec();
SQLiteDatabase.openOrCreateDatabase(GLOBAL_DB, null).close();
Shell.su("db_restore").exec();
}
Shell.su("db_setup " + Process.myUid()).exec();
}
// 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) {
createTables(db);
oldVersion = 3;
@@ -143,13 +140,13 @@ public class MagiskDatabaseHelper {
}
if (oldVersion == 5) {
setSettings(Const.Key.SU_FINGERPRINT,
mm.prefs.getBoolean(Const.Key.SU_FINGERPRINT, false) ? 1 : 0);
Data.MM().prefs.getBoolean(Const.Key.SU_FINGERPRINT, false) ? 1 : 0);
++oldVersion;
}
}
// Remove everything, we do not support downgrade
public void onDowngrade(SQLiteDatabase db) {
private void onDowngrade(SQLiteDatabase db) {
Utils.toast(R.string.su_db_corrupt, Toast.LENGTH_LONG);
db.execSQL("DROP TABLE IF EXISTS " + POLICY_TABLE);
db.execSQL("DROP TABLE IF EXISTS " + LOG_TABLE);
@@ -178,6 +175,11 @@ public class MagiskDatabaseHelper {
"(key TEXT, value INT, PRIMARY KEY(key))");
}
public void flush() {
db.close();
init();
}
public void clearOutdated() {
// Clear outdated policies
db.delete(POLICY_TABLE, Utils.fmt("until > 0 AND until < %d", System.currentTimeMillis() / 1000), null);

View File

@@ -16,7 +16,7 @@ import java.util.Set;
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 SQLiteDatabase mDb;
@@ -40,21 +40,14 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
try {
if (oldVersion < 3) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
"(id TEXT, name TEXT, version TEXT, versionCode INT, minMagisk INT, " +
"author TEXT, description TEXT, repo_name TEXT, last_update INT, " +
"PRIMARY KEY(id))");
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
oldVersion = 3;
}
} catch (Exception e) {
e.printStackTrace();
// Reset database
onDowngrade(db, DATABASE_VER, 0);
if (oldVersion != newVersion) {
// Nuke old DB and create new table
db.execSQL("DROP TABLE IF EXISTS " + TABLE_NAME);
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
"(id TEXT, name TEXT, version TEXT, versionCode INT, minMagisk INT, " +
"author TEXT, description TEXT, last_update INT, PRIMARY KEY(id))");
mm.prefs.edit().remove(Const.Key.ETAG_KEY).apply();
}
}
@@ -75,8 +68,7 @@ public class RepoDatabaseHelper extends SQLiteOpenHelper {
}
public void removeRepo(Repo repo) {
mDb.delete(TABLE_NAME, "repo_name=?", new String[] { repo.getRepoName() });
notifyAdapter();
removeRepo(repo.getId());
}
public void removeRepo(Iterable<String> list) {

View File

@@ -2,12 +2,11 @@ package com.topjohnwu.magisk.fragments;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
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;
@@ -15,14 +14,11 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.TabFragmentAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import androidx.viewpager.widget.ViewPager;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class LogFragment extends BaseFragment {
private Unbinder unbinder;
@BindView(R.id.container) ViewPager viewPager;
@BindView(R.id.tab) TabLayout tab;
@@ -31,7 +27,7 @@ public class LogFragment extends BaseFragment {
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_log, container, false);
unbinder = ButterKnife.bind(this, v);
unbinder = new LogFragment_ViewBinding(this, v);
((MainActivity) requireActivity()).toolbar.setElevation(0);
@@ -48,11 +44,4 @@ public class LogFragment extends BaseFragment {
return v;
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@@ -5,11 +5,6 @@ import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.CardView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -41,20 +36,22 @@ import com.topjohnwu.magisk.utils.Topic;
import com.topjohnwu.superuser.Shell;
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.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
public class MagiskFragment extends BaseFragment
implements SwipeRefreshLayout.OnRefreshListener, ExpandableView, Topic.Subscriber {
private Container expandableContainer = new Container();
private Unbinder unbinder;
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;
@@ -85,7 +82,7 @@ public class MagiskFragment extends BaseFragment
@BindColor(R.color.red500) int colorBad;
@BindColor(R.color.green500) int colorOK;
@BindColor(R.color.yellow500) int colorWarn;
@BindColor(R.color.grey500) int colorNeutral;
@BindColor(R.color.green500) int colorNeutral;
@BindColor(R.color.blue500) int colorInfo;
@OnClick(R.id.safetyNet_title)
@@ -136,7 +133,7 @@ public class MagiskFragment extends BaseFragment
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_magisk, container, false);
unbinder = ButterKnife.bind(this, v);
unbinder = new MagiskFragment_ViewBinding(this, v);
requireActivity().setTitle(R.string.magisk);
expandableContainer.expandLayout = expandLayout;
@@ -196,12 +193,6 @@ public class MagiskFragment extends BaseFragment
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public Container getContainer() {
return expandableContainer;
@@ -223,11 +214,10 @@ public class MagiskFragment extends BaseFragment
boolean hasNetwork = Download.checkNetworkStatus(mm);
boolean hasRoot = Shell.rootAccess();
boolean isUpToDate = Data.magiskVersionCode > Const.MAGISK_VER.UNIFIED;
magiskUpdate.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;

View File

@@ -1,10 +1,6 @@
package com.topjohnwu.magisk.fragments;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -17,13 +13,14 @@ import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
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.ButterKnife;
import butterknife.Unbinder;
public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber {
private Unbinder unbinder;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
SearchView search;
@@ -42,7 +39,7 @@ public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
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);
@@ -76,12 +73,6 @@ public class MagiskHideFragment extends BaseFragment implements Topic.Subscriber
search.setOnQueryTextListener(searchListener);
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public int[] getSubscribedTopics() {
return new int[] {Topic.MAGISK_HIDE_DONE};

View File

@@ -2,8 +2,6 @@ package com.topjohnwu.magisk.fragments;
import android.Manifest;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
@@ -16,6 +14,7 @@ import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
import com.google.android.material.snackbar.Snackbar;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.BaseFragment;
@@ -28,14 +27,11 @@ import java.io.File;
import java.io.IOException;
import java.util.Calendar;
import androidx.annotation.Nullable;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class MagiskLogFragment extends BaseFragment {
private Unbinder unbinder;
@BindView(R.id.txtLog) TextView txtLog;
@BindView(R.id.svLog) ScrollView svLog;
@BindView(R.id.hsvLog) HorizontalScrollView hsvLog;
@@ -45,7 +41,7 @@ public class MagiskLogFragment extends BaseFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_magisk_log, container, false);
unbinder = ButterKnife.bind(this, view);
unbinder = new MagiskLogFragment_ViewBinding(this, view);
setHasOptionsMenu(true);
txtLog.setTextIsSelectable(true);
return view;
@@ -63,12 +59,6 @@ public class MagiskLogFragment extends BaseFragment {
readLogs();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_log, menu);
@@ -91,7 +81,7 @@ public class MagiskLogFragment extends BaseFragment {
}
}
public void readLogs() {
private void readLogs() {
Shell.su("cat " + Const.MAGISK_LOG + " | tail -n 5000").submit(result -> {
progressBar.setVisibility(View.GONE);
if (result.getOut().isEmpty())
@@ -103,7 +93,7 @@ public class MagiskLogFragment extends BaseFragment {
});
}
public void saveLogs() {
private void saveLogs() {
Calendar now = Calendar.getInstance();
String filename = Utils.fmt("magisk_log_%04d%02d%02d_%02d%02d%02d.log",
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
@@ -121,7 +111,7 @@ public class MagiskLogFragment extends BaseFragment {
SnackbarMaker.make(txtLog, logFile.getPath(), Snackbar.LENGTH_SHORT).show());
}
public void clearLogs() {
private void clearLogs() {
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();

View File

@@ -4,10 +4,6 @@ import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -17,6 +13,7 @@ import android.view.ViewGroup;
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;
@@ -30,19 +27,21 @@ import java.util.ArrayList;
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.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
private Unbinder unbinder;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
@OnClick(R.id.fab)
public void selectFile() {
void selectFile() {
runWithPermission(new String[] { Manifest.permission.WRITE_EXTERNAL_STORAGE }, () -> {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("application/zip");
@@ -56,7 +55,7 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_modules, container, false);
unbinder = ButterKnife.bind(this, view);
unbinder = new ModulesFragment_ViewBinding(this, view);
setHasOptionsMenu(true);
mSwipeRefreshLayout.setOnRefreshListener(() -> {
@@ -95,18 +94,12 @@ public class ModulesFragment extends BaseFragment implements Topic.Subscriber {
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == Const.ID.FETCH_ZIP && resultCode == Activity.RESULT_OK && data != null) {
// 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);
startActivity(intent);
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_reboot, menu);

View File

@@ -2,10 +2,6 @@ package com.topjohnwu.magisk.fragments;
import android.app.AlertDialog;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -26,13 +22,14 @@ 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.ButterKnife;
import butterknife.Unbinder;
public class ReposFragment extends BaseFragment implements Topic.Subscriber {
private Unbinder unbinder;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@@ -49,7 +46,7 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_repos, container, false);
unbinder = ButterKnife.bind(this, view);
unbinder = new ReposFragment_ViewBinding(this, view);
mSwipeRefreshLayout.setRefreshing(true);
recyclerView.setVisibility(View.GONE);
@@ -123,6 +120,5 @@ public class ReposFragment extends BaseFragment implements Topic.Subscriber {
public void onDestroyView() {
super.onDestroyView();
mm.repoDB.unregisterAdapter();
unbinder.unbind();
}
}

View File

@@ -1,23 +1,11 @@
package com.topjohnwu.magisk.fragments;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.hardware.fingerprint.FingerprintManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v14.preference.SwitchPreference;
import android.support.v7.app.AlertDialog;
import android.support.v7.preference.ListPreference;
import android.support.v7.preference.Preference;
import android.support.v7.preference.PreferenceCategory;
import android.support.v7.preference.PreferenceFragmentCompat;
import android.support.v7.preference.PreferenceScreen;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -30,7 +18,6 @@ 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.components.CustomAlertDialog;
import com.topjohnwu.magisk.receivers.DownloadReceiver;
import com.topjohnwu.magisk.utils.Download;
import com.topjohnwu.magisk.utils.FingerprintHelper;
@@ -44,6 +31,17 @@ 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 {
@@ -126,7 +124,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
fingerprint.setSummary(R.string.disable_fingerprint);
}
if (Data.magiskVersionCode >= Const.MAGISK_VER.MANAGER_HIDE) {
if (Shell.rootAccess()) {
if (mm.getPackageName().equals(Const.ORIG_PKG_NAME)) {
hideManager.setOnPreferenceClickListener((pref) -> {
PatchAPK.hideManager(requireActivity());
@@ -173,8 +171,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
if (!Shell.rootAccess()) {
prefScreen.removePreference(magiskCategory);
generalCatagory.removePreference(hideManager);
} else if (Data.magiskVersionCode < Const.MAGISK_VER.UNIFIED) {
prefScreen.removePreference(magiskCategory);
}
}
@@ -221,7 +217,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
setSummary();
switch (key) {
case Const.Key.DARK_THEME:
Topic.publish(false, Topic.RELOAD_ACTIVITY);
requireActivity().recreate();
break;
case Const.Key.COREONLY:
if (prefs.getBoolean(key, false)) {
@@ -253,7 +249,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
break;
case Const.Key.LOCALE:
LocaleManager.setLocale(mm);
Topic.publish(false, Topic.RELOAD_ACTIVITY);
requireActivity().recreate();
break;
case Const.Key.UPDATE_CHANNEL:
case Const.Key.CUSTOM_CHANNEL:
@@ -272,54 +268,11 @@ public class SettingsFragment extends PreferenceFragmentCompat
case Const.Key.SU_FINGERPRINT:
boolean checked = ((SwitchPreference) preference).isChecked();
((SwitchPreference) preference).setChecked(!checked);
CustomAlertDialog dialog = new CustomAlertDialog(requireActivity());
CustomAlertDialog.ViewHolder vh = dialog.getViewHolder();
Drawable fingerprint = getResources().getDrawable(R.drawable.ic_fingerprint);
fingerprint.setBounds(0, 0, Utils.dpInPx(50), Utils.dpInPx(50));
Resources.Theme theme = requireActivity().getTheme();
TypedArray ta = theme.obtainStyledAttributes(new int[] {R.attr.imageColorTint});
fingerprint.setTint(ta.getColor(0, Color.GRAY));
ta.recycle();
vh.messageView.setCompoundDrawables(null, null, null, fingerprint);
vh.messageView.setCompoundDrawablePadding(Utils.dpInPx(20));
vh.messageView.setGravity(Gravity.CENTER);
try {
FingerprintHelper helper = new FingerprintHelper() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(helpString);
}
@Override
public void onAuthenticationFailed() {
vh.messageView.setTextColor(Color.RED);
vh.messageView.setText(R.string.auth_fail);
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
dialog.dismiss();
((SwitchPreference) preference).setChecked(checked);
mm.mDB.setSettings(key, checked ? 1 : 0);
}
};
dialog.setMessage(R.string.auth_fingerprint)
.setNegativeButton(R.string.close, (d, w) -> helper.cancel())
.setOnCancelListener(d -> helper.cancel())
.show();
helper.authenticate();
} catch (Exception e) {
e.printStackTrace();
Utils.toast(R.string.auth_fail, Toast.LENGTH_SHORT);
}
FingerprintHelper.showAuthDialog(requireActivity(), () -> {
((SwitchPreference) preference).setChecked(checked);
Data.suFingerprint = checked;
mm.mDB.setSettings(key, checked ? 1 : 0);
});
break;
}
return true;
@@ -352,4 +305,38 @@ public class SettingsFragment extends PreferenceFragmentCompat
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());
}
}
}

View File

@@ -1,8 +1,6 @@
package com.topjohnwu.magisk.fragments;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
@@ -15,16 +13,15 @@ import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.adapters.SuLogAdapter;
import com.topjohnwu.magisk.components.BaseFragment;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class SuLogFragment extends BaseFragment {
@BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
private Unbinder unbinder;
private SuLogAdapter adapter;
@Override
@@ -44,7 +41,7 @@ public class SuLogFragment extends BaseFragment {
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_su_log, container, false);
unbinder = ButterKnife.bind(this, v);
unbinder = new SuLogFragment_ViewBinding(this, v);
adapter = new SuLogAdapter(mm.mDB);
recyclerView.setAdapter(adapter);
@@ -79,10 +76,4 @@ public class SuLogFragment extends BaseFragment {
return true;
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@@ -2,8 +2,6 @@ package com.topjohnwu.magisk.fragments;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -16,24 +14,24 @@ import com.topjohnwu.magisk.container.Policy;
import java.util.List;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class SuperuserFragment extends BaseFragment {
private Unbinder unbinder;
private PackageManager pm;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
private PackageManager pm;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_superuser, container, false);
unbinder = ButterKnife.bind(this, view);
unbinder = new SuperuserFragment_ViewBinding(this, view);
pm = getActivity().getPackageManager();
pm = requireActivity().getPackageManager();
return view;
}
@@ -49,12 +47,6 @@ public class SuperuserFragment extends BaseFragment {
displayPolicyList();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
private void displayPolicyList() {
List<Policy> policyList = mm.mDB.getPolicyList(pm);

View File

@@ -5,14 +5,31 @@ import android.content.Context;
import android.content.Intent;
import android.text.TextUtils;
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 {
@Override
public void onReceive(Context context, Intent intent) {
if (TextUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED))
OnBootService.enqueueWork(context);
if (TextUtils.equals(intent.getAction(), Intent.ACTION_BOOT_COMPLETED)) {
switch (intent.getExtras().getString("action", "boot")) {
case "request":
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;
}
}
}
}

View File

@@ -7,7 +7,6 @@ import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.support.annotation.RequiresApi;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
@@ -18,6 +17,8 @@ import com.topjohnwu.superuser.Shell;
import java.util.ArrayList;
import androidx.annotation.RequiresApi;
public class ShortcutReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
@@ -36,7 +37,7 @@ public class ShortcutReceiver extends BroadcastReceiver {
Data.multiuserMode == Const.Value.MULTIUSER_MODE_OWNER_MANAGED)) {
shortCuts.add(new ShortcutInfo.Builder(mm, "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")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
@@ -44,11 +45,10 @@ public class ShortcutReceiver extends BroadcastReceiver {
.setRank(0)
.build());
}
if (root && Data.magiskVersionCode >= Const.MAGISK_VER.UNIFIED
&& mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
if (root && mm.prefs.getBoolean(Const.Key.MAGISKHIDE, false)) {
shortCuts.add(new ShortcutInfo.Builder(mm, "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")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
@@ -59,7 +59,7 @@ public class ShortcutReceiver extends BroadcastReceiver {
if (!mm.prefs.getBoolean(Const.Key.COREONLY, false) && root && Data.magiskVersionCode >= 0) {
shortCuts.add(new ShortcutInfo.Builder(mm, "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")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))
@@ -68,7 +68,7 @@ public class ShortcutReceiver extends BroadcastReceiver {
.build());
shortCuts.add(new ShortcutInfo.Builder(mm, "downloads")
.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")
.setAction(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK))

View File

@@ -2,18 +2,20 @@ package com.topjohnwu.magisk.services;
import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v4.app.JobIntentService;
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, OnBootService.class, Const.ID.ONBOOT_SERVICE_ID, new Intent());
enqueueWork(context, Data.classMap.get(OnBootService.class), Const.ID.ONBOOT_SERVICE_ID, new Intent());
}
@Override

View File

@@ -1,297 +1,21 @@
package com.topjohnwu.magisk.superuser;
import android.content.ContentValues;
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.CountDownTimer;
import android.os.FileObserver;
import android.text.TextUtils;
import android.view.View;
import android.view.Window;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Spinner;
import android.widget.TextView;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.ParallelTask;
import com.topjohnwu.magisk.SuRequestActivity;
import com.topjohnwu.magisk.components.BaseActivity;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.utils.FingerprintHelper;
import java.io.DataInputStream;
import java.io.IOException;
import butterknife.BindView;
import butterknife.ButterKnife;
public class RequestActivity extends BaseActivity {
@BindView(R.id.su_popup) LinearLayout suPopup;
@BindView(R.id.timeout) Spinner timeout;
@BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.app_name) TextView appNameView;
@BindView(R.id.package_name) TextView packageNameView;
@BindView(R.id.grant_btn) Button grant_btn;
@BindView(R.id.deny_btn) Button deny_btn;
@BindView(R.id.fingerprint) ImageView fingerprintImg;
@BindView(R.id.warning) TextView warning;
private String socketPath;
private LocalSocket socket;
private PackageManager pm;
private boolean hasTimeout;
private Policy policy;
private CountDownTimer timer;
private FingerprintHelper fingerprintHelper;
@Override
public int getDarkTheme() {
return R.style.SuRequest_Dark;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
pm = getPackageManager();
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 (Data.suResponseType) {
case Const.Value.SU_AUTO_DENY:
handleAction(Policy.DENY, 0);
return;
case Const.Value.SU_AUTO_ALLOW:
handleAction(Policy.ALLOW, 0);
return;
case Const.Value.SU_PROMPT:
default:
}
// If not interactive, response directly
if (policy.policy != Policy.INTERACTIVE) {
handleAction();
return;
}
setContentView(R.layout.activity_request);
ButterKnife.bind(this);
appIcon.setImageDrawable(policy.info.loadIcon(pm));
appNameView.setText(policy.appName);
packageNameView.setText(policy.packageName);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,
R.array.allow_timeout, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
timeout.setAdapter(adapter);
timer = new CountDownTimer(Data.suRequestTimeout * 1000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
deny_btn.setText(getString(R.string.deny_with_str, "(" + millisUntilFinished / 1000 + ")"));
}
@Override
public void onFinish() {
deny_btn.setText(getString(R.string.deny_with_str, "(0)"));
handleAction(Policy.DENY);
}
};
boolean useFingerprint = Data.suFingerprint && FingerprintHelper.canUseFingerprint();
if (useFingerprint) {
try {
fingerprintHelper = new FingerprintHelper() {
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
warning.setText(errString);
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
warning.setText(helpString);
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
handleAction(Policy.ALLOW);
}
@Override
public void onAuthenticationFailed() {
warning.setText(R.string.auth_fail);
}
};
fingerprintHelper.authenticate();
} catch (Exception e) {
e.printStackTrace();
useFingerprint = false;
}
}
if (!useFingerprint) {
grant_btn.setOnClickListener(v -> {
handleAction(Policy.ALLOW);
timer.cancel();
});
grant_btn.requestFocus();
}
grant_btn.setVisibility(useFingerprint ? View.GONE : View.VISIBLE);
fingerprintImg.setVisibility(useFingerprint ? View.VISIBLE : View.GONE);
deny_btn.setOnClickListener(v -> {
handleAction(Policy.DENY);
timer.cancel();
});
suPopup.setOnClickListener(v -> cancelTimeout());
timeout.setOnTouchListener((v, event) -> cancelTimeout());
if (hasTimeout) {
timer.start();
} else {
cancelTimeout();
}
}
@Override
public void onBackPressed() {
if (policy != null) {
handleAction(Policy.DENY);
} else {
finish();
}
}
void handleAction() {
String response;
if (policy.policy == Policy.ALLOW) {
response = "socket:ALLOW";
} else {
response = "socket:DENY";
}
try {
socket.getOutputStream().write((response).getBytes());
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
Intent intent = new Intent(this, Data.classMap.get(SuRequestActivity.class))
.putExtra("socket", getIntent().getStringExtra("socket"))
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finish();
}
void handleAction(int action) {
handleAction(action, Const.Value.timeoutList[timeout.getSelectedItemPosition()]);
}
void handleAction(int action, int time) {
policy.policy = action;
if (time >= 0) {
policy.until = (time == 0) ? 0 : (System.currentTimeMillis() / 1000 + time * 60);
mm.mDB.addPolicy(policy);
}
handleAction();
}
private class SocketManager extends ParallelTask<Void, Void, Boolean> {
SocketManager(BaseActivity context) {
super(context);
}
@Override
protected Boolean doInBackground(Void... params) {
try {
socket = new LocalSocket();
socket.connect(new LocalSocketAddress(socketPath, LocalSocketAddress.Namespace.FILESYSTEM));
DataInputStream is = new DataInputStream(socket.getInputStream());
ContentValues payload = new ContentValues();
while (true) {
int nameLen = is.readInt();
byte[] nameBytes = new byte[nameLen];
is.readFully(nameBytes);
String name = new String(nameBytes);
if (TextUtils.equals(name, "eof"))
break;
int dataLen = is.readInt();
byte[] dataBytes = new byte[dataLen];
is.readFully(dataBytes);
String data = new String(dataBytes);
payload.put(name, data);
}
if (payload.getAsInteger("uid") == null) {
return false;
}
int uid = payload.getAsInteger("uid");
policy = mm.mDB.getPolicy(uid);
if (policy == null) {
policy = new Policy(uid, pm);
}
/* Never allow com.topjohnwu.magisk (could be malware) */
if (TextUtils.equals(policy.packageName, Const.ORIG_PKG_NAME))
return false;
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
showRequest();
} else {
finish();
}
}
}
}

View File

@@ -3,88 +3,13 @@ package com.topjohnwu.magisk.superuser;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Process;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.container.SuLogEntry;
import com.topjohnwu.magisk.utils.Utils;
import java.util.Date;
import com.topjohnwu.magisk.utils.SuConnector;
public class SuReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
int fromUid, toUid, pid, mode;
String command, action;
Policy policy;
MagiskManager mm = Data.MM();
if (intent == null) return;
mode = intent.getIntExtra("mode", -1);
if (mode < 0) return;
if (mode == Const.Value.NOTIFY_USER_TO_OWNER) {
Utils.toast(R.string.multiuser_hint_owner_request, Toast.LENGTH_LONG);
return;
}
fromUid = intent.getIntExtra("from.uid", -1);
if (fromUid < 0) return;
if (fromUid == Process.myUid()) return; // Don't show anything if it's Magisk Manager
action = intent.getStringExtra("action");
if (action == null) return;
policy = mm.mDB.getPolicy(fromUid);
if (policy == null) {
try {
policy = new Policy(fromUid, mm.getPackageManager());
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return;
}
}
SuLogEntry log = new SuLogEntry(policy);
String message;
switch (action) {
case "allow":
message = mm.getString(R.string.su_allow_toast, policy.appName);
log.action = true;
break;
case "deny":
message = mm.getString(R.string.su_deny_toast, policy.appName);
log.action = false;
break;
default:
return;
}
if (policy.notification && Data.suNotificationType == Const.Value.NOTIFICATION_TOAST)
Utils.toast(message, Toast.LENGTH_SHORT);
if (mode == Const.Value.NOTIFY_NORMAL_LOG && policy.logging) {
toUid = intent.getIntExtra("to.uid", -1);
if (toUid < 0) return;
pid = intent.getIntExtra("pid", -1);
if (pid < 0) return;
command = intent.getStringExtra("command");
if (command == null) return;
log.toUid = toUid;
log.fromPid = pid;
log.command = command;
log.date = new Date();
mm.mDB.addLog(log);
}
SuConnector.handleLogs(intent, 1);
}
}

View File

@@ -1,12 +1,12 @@
package com.topjohnwu.magisk.utils;
import android.support.annotation.Keep;
import com.topjohnwu.utils.SignBoot;
import java.io.FileInputStream;
import java.io.InputStream;
import androidx.annotation.Keep;
public class BootSigner {
@Keep

View File

@@ -1,17 +1,26 @@
package com.topjohnwu.magisk.utils;
import android.annotation.TargetApi;
import android.app.Activity;
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.os.Build;
import android.os.CancellationSignal;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
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.R;
import com.topjohnwu.magisk.components.CustomAlertDialog;
import java.security.KeyStore;
@@ -35,6 +44,56 @@ public abstract class FingerprintHelper {
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 {
KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
manager = Data.MM().getSystemService(FingerprintManager.class);

View File

@@ -1,7 +1,5 @@
package com.topjohnwu.magisk.utils;
import android.support.annotation.Keep;
public interface ISafetyNetHelper {
int RESPONSE_ERR = 0x01;
@@ -10,14 +8,11 @@ public interface ISafetyNetHelper {
int BASIC_PASS = 0x10;
int CTS_PASS = 0x20;
@Keep
void attest();
@Keep
int getVersion();
interface Callback {
@Keep
void onResponse(int responseCode);
}
}

View File

@@ -3,7 +3,6 @@ package com.topjohnwu.magisk.utils;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.AsyncTask;
import android.support.annotation.StringRes;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
@@ -16,6 +15,8 @@ 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();

View File

@@ -4,8 +4,6 @@ import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.TaskStackBuilder;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
@@ -15,15 +13,18 @@ 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, SplashActivity.class);
Intent intent = new Intent(mm, Data.classMap.get(SplashActivity.class));
intent.putExtra(Const.Key.OPEN_SECTION, "magisk");
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mm);
stackBuilder.addParentStack(SplashActivity.class);
stackBuilder.addParentStack(Data.classMap.get(SplashActivity.class));
stackBuilder.addNextIntent(intent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(Const.ID.MAGISK_UPDATE_NOTIFICATION_ID,
PendingIntent.FLAG_UPDATE_CURRENT);
@@ -46,7 +47,7 @@ public class NotificationMgr {
String filename = Utils.fmt("MagiskManager-v%s(%d).apk",
Data.remoteManagerVersionString, Data.remoteManagerVersionCode);
Intent intent = new Intent(mm, ManagerUpdate.class);
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,
@@ -68,7 +69,7 @@ public class NotificationMgr {
public static void dtboPatched() {
MagiskManager mm = Data.MM();
Intent intent = new Intent(mm, RebootReceiver.class);
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);

View File

@@ -1,7 +1,6 @@
package com.topjohnwu.magisk.utils;
import android.content.Context;
import android.support.annotation.NonNull;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
@@ -14,6 +13,8 @@ import com.topjohnwu.superuser.io.SuFile;
import java.io.File;
import java.io.InputStream;
import androidx.annotation.NonNull;
public class RootUtils extends Shell.Initializer {
static {
@@ -49,7 +50,7 @@ public class RootUtils extends Shell.Initializer {
job.add(nonroot);
}
job.add("mount_partitions", "get_flags", "run_migrations").exec();
job.add("mount_partitions", "get_flags", "run_migrations", "export BOOTMODE=true").exec();
Data.keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
Data.keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT"));

View File

@@ -0,0 +1,118 @@
package com.topjohnwu.magisk.utils;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.LocalSocket;
import android.os.Bundle;
import android.os.Process;
import android.text.TextUtils;
import android.widget.Toast;
import com.topjohnwu.magisk.Const;
import com.topjohnwu.magisk.Data;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.Policy;
import com.topjohnwu.magisk.container.SuLogEntry;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Date;
public abstract class SuConnector {
protected LocalSocket socket = new LocalSocket();
private String readString(DataInputStream is) throws IOException {
int len = is.readInt();
byte[] buf = new byte[len];
is.readFully(buf);
return new String(buf);
}
public Bundle readSocketInput() throws IOException {
Bundle bundle = new Bundle();
DataInputStream is = new DataInputStream(socket.getInputStream());
while (true) {
String name = readString(is);
if (TextUtils.equals(name, "eof"))
break;
bundle.putString(name, readString(is));
}
return bundle;
}
protected DataOutputStream getOutputStream() throws IOException {
return new DataOutputStream(socket.getOutputStream());
}
public abstract void response();
public static void handleLogs(Intent intent, int version) {
MagiskManager mm = Data.MM();
if (intent == null) return;
int fromUid = intent.getIntExtra("from.uid", -1);
if (fromUid < 0) return;
if (fromUid == Process.myUid()) return;
Policy policy = mm.mDB.getPolicy(fromUid);
if (policy == null) {
try {
policy = new Policy(fromUid, mm.getPackageManager());
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return;
}
}
SuLogEntry log = new SuLogEntry(policy);
if (version == 1) {
String action = intent.getStringExtra("action");
if (action == null) return;
switch (action) {
case "allow":
log.action = true;
break;
case "deny":
log.action = false;
break;
default:
return;
}
} else {
switch (intent.getIntExtra("policy", -1)) {
case Policy.ALLOW:
log.action = true;
break;
case Policy.DENY:
log.action = false;
break;
default:
return;
}
}
String message = mm.getString(log.action ?
R.string.su_allow_toast : R.string.su_deny_toast, policy.appName);
if (policy.notification && Data.suNotificationType == Const.Value.NOTIFICATION_TOAST)
Utils.toast(message, Toast.LENGTH_SHORT);
if (policy.logging) {
int toUid = intent.getIntExtra("to.uid", -1);
if (toUid < 0) return;
int pid = intent.getIntExtra("pid", -1);
if (pid < 0) return;
String command = intent.getStringExtra("command");
if (command == null) return;
log.toUid = toUid;
log.fromPid = pid;
log.command = command;
log.date = new Date();
mm.mDB.addLog(log);
}
}
}

View File

@@ -1,7 +1,5 @@
package com.topjohnwu.magisk.utils;
import android.support.annotation.IntDef;
import com.topjohnwu.magisk.Data;
import java.lang.annotation.Retention;
@@ -9,23 +7,24 @@ import java.lang.annotation.RetentionPolicy;
import java.util.HashSet;
import java.util.Set;
import androidx.annotation.IntDef;
public class Topic {
public static final int MAGISK_HIDE_DONE = 0;
public static final int RELOAD_ACTIVITY = 1;
public static final int MODULE_LOAD_DONE = 2;
public static final int REPO_LOAD_DONE = 3;
public static final int UPDATE_CHECK_DONE = 4;
public static final int SNET_CHECK_DONE = 5;
public static final int LOCALE_FETCH_DONE = 6;
public static final int MODULE_LOAD_DONE = 1;
public static final int REPO_LOAD_DONE = 2;
public static final int UPDATE_CHECK_DONE = 3;
public static final int SNET_CHECK_DONE = 4;
public static final int LOCALE_FETCH_DONE = 5;
@IntDef({MAGISK_HIDE_DONE, RELOAD_ACTIVITY, MODULE_LOAD_DONE, REPO_LOAD_DONE,
@IntDef({MAGISK_HIDE_DONE, MODULE_LOAD_DONE, REPO_LOAD_DONE,
UPDATE_CHECK_DONE, SNET_CHECK_DONE, LOCALE_FETCH_DONE})
@Retention(RetentionPolicy.SOURCE)
public @interface TopicID {}
// We will not dynamically add topics, so use arrays instead of hash tables
private static Store[] topicList = new Store[7];
private static Store[] topicList = new Store[6];
public static void subscribe(Subscriber sub, @TopicID int... topics) {
for (int topic : topics) {

View File

@@ -6,6 +6,10 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
@@ -18,12 +22,13 @@ import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.container.Module;
import com.topjohnwu.magisk.container.ValueSortedMap;
import com.topjohnwu.magisk.services.UpdateCheckService;
import com.topjohnwu.superuser.io.SuFile;
import java.util.Locale;
import java.util.Map;
import a.n;
public class Utils {
public static int getPrefsInt(SharedPreferences prefs, String key, int def) {
@@ -77,7 +82,7 @@ public class Utils {
if (mm.prefs.getBoolean(Const.Key.CHECK_UPDATES, true)) {
if (scheduler.getAllPendingJobs().isEmpty() ||
Const.UPDATE_SERVICE_VER > mm.prefs.getInt(Const.Key.UPDATE_SERVICE_VER, -1)) {
ComponentName service = new ComponentName(mm, UpdateCheckService.class);
ComponentName service = new ComponentName(mm, n.class);
JobInfo info = new JobInfo.Builder(Const.ID.UPDATE_SERVICE_ID, service)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
@@ -122,4 +127,17 @@ public class Utils {
Topic.publish(Topic.MODULE_LOAD_DONE, moduleMap);
});
}
public static String getAppLabel(ApplicationInfo info, PackageManager pm) {
try {
if (info.labelRes > 0) {
Resources res = pm.getResourcesForApplication(info);
Configuration config = new Configuration();
config.setLocale(LocaleManager.locale);
res.updateConfiguration(config, res.getDisplayMetrics());
return res.getString(info.labelRes);
}
} catch (Exception ignored) {}
return info.loadLabel(pm).toString();
}
}

View File

@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
@@ -54,42 +54,42 @@
android:textAppearance="@style/TextAppearance.AppCompat.Headline"/>
</LinearLayout>
<com.topjohnwu.magisk.components.AboutCardRow
<a.o
android:id="@+id/app_version_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_info_outline"
app:text="@string/app_version"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.o
android:id="@+id/app_changelog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_history"
app:text="@string/app_changelog"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.o
android:id="@+id/app_translators"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_language"
app:text="@string/app_translators"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.o
android:id="@+id/follow_twitter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_twitter"
app:text="@string/follow_twitter"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.o
android:id="@+id/app_source_code"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_github"
app:text="@string/app_source_code"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.o
android:id="@+id/support_thread"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -98,7 +98,7 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

@@ -20,7 +20,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
@@ -40,14 +40,14 @@
android:text="@string/donation"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.o
android:id="@+id/paypal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:icon="@drawable/ic_paypal"
app:text="PayPal"/>
<com.topjohnwu.magisk.components.AboutCardRow
<a.o
android:id="@+id/patreon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -56,7 +56,7 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
</LinearLayout>

View File

@@ -42,7 +42,7 @@
android:visibility="gone">
<Button
android:id="@+id/no_thanks"
android:id="@+id/close"
style="?android:borderlessButtonStyle"
android:layout_width="0dp"
android:layout_height="50dp"

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
@@ -24,7 +24,7 @@
</FrameLayout>
<android.support.design.widget.NavigationView
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
@@ -32,4 +32,4 @@
android:fitsSystemWindows="true"
app:menu="@menu/drawer" />
</android.support.v4.widget.DrawerLayout>
</androidx.drawerlayout.widget.DrawerLayout>

View File

@@ -3,7 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/su_popup"
tools:context=".superuser.RequestActivity"
tools:context=".SuRequestActivity"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:minWidth="350dp"

View File

@@ -7,7 +7,7 @@
android:orientation="vertical"
tools:context="com.topjohnwu.magisk.fragments.LogFragment">
<android.support.design.widget.TabLayout
<com.google.android.material.tabs.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
@@ -17,10 +17,10 @@
android:elevation="4dp"
android:visibility="gone">
</android.support.design.widget.TabLayout>
</com.google.android.material.tabs.TabLayout>
<android.support.v4.view.ViewPager
<androidx.viewpager.widget.ViewPager
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="@+id/container"/>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/swipeRefreshLayout"
@@ -18,7 +18,7 @@
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:id="@+id/core_only_notice"
style="?attr/cardStyle"
android:layout_width="match_parent"
@@ -55,9 +55,9 @@
android:text="@string/settings_core_only_title"
android:textStyle="bold" />
</RelativeLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
style="?attr/cardStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -141,9 +141,9 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:id="@+id/safetyNet_card"
style="?attr/cardStyle"
android:layout_width="match_parent"
@@ -259,9 +259,9 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:id="@+id/install_option_card"
style="?attr/cardStyle"
android:layout_width="match_parent"
@@ -308,9 +308,9 @@
android:text="@string/keep_dm_verity" />
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:id="@+id/install_button"
style="?attr/cardStyle"
android:layout_width="match_parent"
@@ -361,9 +361,9 @@
</RelativeLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
android:id="@+id/uninstall_button"
style="?attr/cardStyle"
android:layout_width="match_parent"
@@ -392,10 +392,10 @@
android:textSize="20sp"
android:textStyle="bold" />
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>
</LinearLayout>
</ScrollView>
</android.support.v4.widget.SwipeRefreshLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/swipeRefreshLayout"
@@ -12,15 +12,15 @@ android:orientation="vertical">
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:dividerHeight="@dimen/card_divider_space"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
@@ -9,20 +9,20 @@
android:orientation="vertical">
<android.support.design.widget.CoordinatorLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordinator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:dividerHeight="@dimen/card_divider_space"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
<TextView
android:id="@+id/empty_rv"
@@ -36,7 +36,7 @@
android:textStyle="italic"
android:visibility="gone" />
<android.support.design.widget.FloatingActionButton
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -47,6 +47,6 @@
tools:fabSize="normal"
tools:pressedTranslationZ="12dp" />
</android.support.design.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</android.support.v4.widget.SwipeRefreshLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipeRefreshLayout"
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
@@ -23,13 +23,13 @@
android:textStyle="italic"
android:visibility="gone" />
<android.support.v7.widget.RecyclerView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dividerHeight="@dimen/card_divider_space"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

View File

@@ -18,11 +18,11 @@
android:textStyle="italic"
android:visibility="gone" />
<android.support.v7.widget.RecyclerView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dividerHeight="@dimen/card_divider_space"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</LinearLayout>

View File

@@ -17,11 +17,11 @@
android:textStyle="italic"
android:visibility="gone" />
<android.support.v7.widget.RecyclerView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:dividerHeight="@dimen/card_divider_space"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</LinearLayout>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
@@ -73,6 +73,6 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@@ -110,4 +110,4 @@
</RelativeLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
style="?attr/cardStyle"
@@ -167,6 +167,6 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
style="?attr/cardStyle"
@@ -93,6 +93,6 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
<androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
style="?attr/cardStyle"
@@ -140,6 +140,6 @@
</LinearLayout>
</android.support.v7.widget.CardView>
</androidx.cardview.widget.CardView>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar android:id="@+id/toolbar"
<androidx.appcompat.widget.Toolbar android:id="@+id/toolbar"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"

View File

@@ -1,9 +1,7 @@
### v5.9.0
- No more on boot notifications
- Support new mechanism for installing to inactive slot for OTAs on A/B devices
- Fix restore Magisk Manager settings on Android P
- Verify existing file checksums to prevent unnecessary re-downloads
- Update SNET extension to use new Google API, fix "Invalid Response" errors
- Move fingerprint settings to magisk database to prevent the settings to be easily removed
- Fingerprint settings are now guarded with fingerprint authentications before it can get changed
- Prevent any files to be downloaded to `/sdcard/MagiskManager`
### 6.0.1
- Update to use new online module's organizing method
- When fingerprint authentication is enabled, toggling root permissions in "Superuser" section now requires fingerprint beforehand
- Fix crashes when entering MagiskHide section on some devices
- Remove support to Magisk version lower than v15.0
- Ask storage permissions before patching stock boot image
- Update dark theme CardView color

View File

@@ -10,11 +10,11 @@
<!--Status Fragment-->
<string name="magisk_version_error">Magisk ist nicht installiert</string>
<string name="checking_for_updates">Suche nach Aktualisierungen</string>
<string name="checking_for_updates">Suche nach Aktualisierungen...</string>
<string name="magisk_update_available">Magisk %1$s ist verfügbar!</string>
<string name="invalid_update_channel">Nicht valider Aktualisierungskanal</string>
<string name="invalid_update_channel">Ungültiger Aktualisierungskanal</string>
<string name="safetyNet_check_text">SafetyNet-Status abfragen</string>
<string name="checking_safetyNet_status">Prüfe SafetyNet-Status</string>
<string name="checking_safetyNet_status">Prüfe SafetyNet-Status...</string>
<string name="safetyNet_check_success">SafetyNet-Test erfolgreich</string>
<string name="safetyNet_api_error">SafetyNet API Fehler</string>
<string name="safetyNet_res_invalid">Die Antwort ist ungültig</string>
@@ -27,7 +27,7 @@
<string name="install_magisk_title">Neueste Version: %1$s</string>
<string name="uninstall">Deinstallieren</string>
<string name="uninstall_magisk_title">Magisk deinstallieren</string>
<string name="uninstall_magisk_msg">Alle Module werden deaktiviert/entfernt. Root wird entfernt und wenn die Daten nicht verschlüsselt sind, werden sie möglicherweise verschlüsselt.</string>
<string name="uninstall_magisk_msg">Alle Module werden deaktiviert/entfernt. Root wird entfernt und möglicherweise werden Ihre Daten verschlüsseln, falls Ihre Daten derzeit nicht verschlüsselt sind.</string>
<string name="update">Aktualisierung %1$s</string>
<!--Module Fragment-->
@@ -39,7 +39,7 @@
<string name="disable_file_created">Modul wird beim nächsten Neustart deaktiviert</string>
<string name="disable_file_removed">Modul wird beim nächsten Neustart aktiviert</string>
<string name="author">Erstellt von %1$s</string>
<string name="reboot_recovery">Neustart in die Recovery</string>
<string name="reboot_recovery">Neustart in das Recovery</string>
<string name="reboot_bootloader">Neustart in den Bootloader</string>
<string name="reboot_download">Neustart in den Download-Modus</string>
@@ -50,24 +50,25 @@
<string name="updated_on">Zuletzt aktualisiert: %1$s</string>
<string name="sorting_order">Sortierung</string>
<string name="sort_by_name">Nach Namen sortiert</string>
<string name="sort_by_update">Nach Aktualisierung sortiert</string>
<string name="sort_by_update">Nach Aktualisierungsdatum sortiert</string>
<!--Log Fragment-->
<string name="menuSaveLog">Protokoll speichern</string>
<string name="menuSaveLog">Log speichern</string>
<string name="menuReload">Log erneut laden</string>
<string name="menuClearLog">Log löschen</string>
<string name="logs_cleared">Log gelöscht</string>
<string name="log_is_empty">Log ist leer</string>
<!--About Activity-->
<string name="about">Über</string>
<string name="app_changelog">Änderungen</string>
<string name="translators">skalnet, c727, jenslody</string>
<string name="about">Info</string>
<string name="app_changelog">Änderungsprotokoll</string>
<string name="translators">skalnet, c727, jenslody, SuperSandro2000</string>
<string name="app_version">Version</string>
<string name="app_source_code">Quelltext</string>
<string name="donation">Spende</string>
<string name="donation">Spenden</string>
<string name="app_translators">Übersetzer</string>
<string name="support_thread">Hilfeforum</string>
<string name="follow_twitter">Folge mir auf Twitter</string>
<!--Toasts, Dialogs-->
<string name="close">Schließen</string>
@@ -75,12 +76,12 @@
<string name="repo_install_msg">Möchtest du %1$s installieren?</string>
<string name="download">Herunterladen</string>
<string name="reboot">Neustart</string>
<string name="magisk_update_title">Neue Magisk-Aktualisierung verfügbar!</string>
<string name="settings_reboot_toast">Neustarten, um die Änderungen anzuwenden</string>
<string name="release_notes">Änderungen</string>
<string name="repo_cache_cleared">Repo-Cache gelöscht</string>
<string name="magisk_update_title">Neue Magisk Update verfügbar!</string>
<string name="settings_reboot_toast">Neustarten, um die Änderungen zu übernehmen</string>
<string name="release_notes">Versionshinweise</string>
<string name="repo_cache_cleared">Repo-Cache geleert</string>
<string name="process_error">Prozessfehler</string>
<string name="internal_storage">Die zip-Datei ist gespeichert unter:\n[Interner Speicher]%1$s</string>
<string name="internal_storage">Die Zip-Datei ist gespeichert unter:\n[Interner Speicher]%1$s</string>
<string name="zip_download_title">Herunterladen</string>
<string name="zip_download_msg">Lade Zip-Datei herunter (%1$d%%) …</string>
<string name="zip_process_title">Verarbeite</string>
@@ -88,30 +89,34 @@
<string name="manager_update_title">Aktualisierung für Magisk Manager verfügbar!</string>
<string name="manager_download_install">Herunterladen und installieren</string>
<string name="dtbo_patched_title">DTBO wurde gepatched!</string>
<string name="dtbo_patched_reboot">Magisk Manager hat dtbo.img gepatched, bitte Neustart durchführen</string>
<string name="magisk_updates">Magisk Aktualisierungen</string>
<string name="dtbo_patched_reboot">Magisk Manager hat dtbo.img gepatched, bitte neustarten</string>
<string name="magisk_updates">Magisk Update</string>
<string name="flashing">Flashing</string>
<string name="hide_manager_toast">Verberge Magisk Manager…</string>
<string name="hide_manager_toast2">Dies könnte einige Zeit in Anspruch nehmen…</string>
<string name="hide_manager_toast2">Das könnte einige Zeit dauern...</string>
<string name="hide_manager_fail_toast">Verbergen von Magisk Manager fehlgeschlagen…</string>
<string name="download_zip_only">Zip-Datei nur herunterladen</string>
<string name="open_link_failed_toast">Es wurde keine Anwendung gefunden, um diesen Link zu öffnen...</string>
<string name="download_zip_only">Nur Zip-Datei herunterladen</string>
<string name="patch_boot_file">Boot-Image-Datei patchen</string>
<string name="direct_install">Direkt installieren (empfohlen)</string>
<string name="install_inactive_slot">Installiere auf inaktiven Slot (Nach OTA)</string>
<string name="warning">Warnung</string>
<string name="install_inactive_slot_msg">Dein Gerät wird GEZWUNGEN in den aktuell inaktiven Slot zu starten, nachdem nach dem ein Neustart durchgeführt wurde!\nBenutze diese Option nur, nachdem das OTA beendet wurde.\nFortsetzen?</string>
<string name="select_method">Methode auswählen</string>
<string name="no_boot_file_patch_support">Magisk Zielversion unterstützt das Patchen des Boot-Images nicht</string>
<string name="boot_file_patch_msg">Selektiere das originale Boot image dump im Format .img oder .img.tar</string>
<string name="no_boot_file_patch_support">Magisk Zielversion unterstützt das Patchen von Boot-Images nicht</string>
<string name="boot_file_patch_msg">Wähle das original Boot-Image-Dump im Format .img oder .img.tar</string>
<string name="complete_uninstall">Komplette Deinstallation</string>
<string name="restore_img">Images wiederherstellen</string>
<string name="restore_img_msg">Wiederherstellen...</string>
<string name="restore_done">Wiederherstellung durchgeführt!</string>
<string name="restore_done">Wiederherstellung erfolgreich!</string>
<string name="restore_fail">Kein Original Backup vorhanden!</string>
<string name="proprietary_title">Lade proprietären Code herunter</string>
<string name="proprietary_notice">Magisk Manager ist FOSS und enthält keinen proprietären SafetyNet API Code von Google. Magisk Manager erlauben eine Erweiterung (enthält GoogleApiClient) für SafetyNet-Checks herunterzuladen?</string>
<string name="su_db_corrupt">Datenbank für SU ist fehlerhaft, es wird eine neue erstellt</string>
<string name="su_db_corrupt">SU-Datenbank ist fehlerhaft, es wird eine neue erstellt</string>
<string name="setup_done">Einrichtung abgeschlossen</string>
<string name="setup_fail">Einrichtung fehlgeschlagen</string>
<string name="env_fix_title">Zusätzliche Einrichtung erforderlich</string>
<string name="env_fix_msg">Um Magisk ordnungsgemäß zu nutzen, benötigt das Geräte eine zusätzliche Einrichtung. Magisk Einrichtungs-zip wird heruntergeladen, fortsetzen?</string>
<string name="env_fix_msg">Ihr Gerät benötigt zusätzliche Einrichtung, damit Magisk ordnungsgemäß funktioniert. Es wird eine Magisk-Installations-Zip wird heruntergeladen, fortfahren?</string>
<string name="setup_title">Zusätzliche Einrichtung</string>
<string name="setup_msg">Umgebungseinrichtung läuft…</string>
@@ -119,12 +124,12 @@
<string name="settings_general_category">Allgemein</string>
<string name="settings_dark_theme_title">Dunkles Theme</string>
<string name="settings_dark_theme_summary">Dunkles Theme aktivieren</string>
<string name="settings_clear_cache_title">Repo-Cache löschen</string>
<string name="settings_clear_cache_summary">Löscht die zwischengespeicherten Informationen der Online-Repos. Erzwingt eine Online-Aktualisierung</string>
<string name="settings_clear_cache_title">Repo-Cache leeren</string>
<string name="settings_clear_cache_summary">Löscht die zwischengespeicherten Informationen des Online-Repos. Erzwingt eine Aktualisierung</string>
<string name="settings_hide_manager_title">Magisk Manager verbergen</string>
<string name="settings_hide_manager_summary">Magisk Manager mit zufälligem Paketnamen neu packen</string>
<string name="settings_restore_manager_title">Stelle Magisk Manager wieder her</string>
<string name="settings_restore_manager_summary">Stelle Magisk Manager mit ursprünglichem Paket wieder her</string>
<string name="settings_restore_manager_title">Magisk Manager wiederherstellen</string>
<string name="settings_restore_manager_summary">Stellt Magisk Manager mit ursprünglichem Paketnamen wieder her</string>
<string name="language">Sprache</string>
<string name="system_default">(Systemstandard)</string>
<string name="settings_update">Aktualisierungs-Einstellungen</string>
@@ -134,13 +139,13 @@
<string name="settings_update_stable">Stabil</string>
<string name="settings_update_beta">Beta</string>
<string name="settings_update_custom">Benutzerdefiniert</string>
<string name="settings_update_custom_msg">Gebe eine benutzerdefinierte URL ein</string>
<string name="settings_update_custom_msg">Gib eine benutzerdefinierte URL ein</string>
<string name="settings_boot_format_title">Ausgabeformat des gepatchten Boot-Images</string>
<string name="settings_boot_format_summary">Wähle das Ausgabeformat des gepatchten Boot-Images.\nWähle .img, um mit \"fastboot/download mode\" zu flashen; wähle .img.tar zum Flashen mit ODIN.</string>
<string name="settings_core_only_title">Nur Kernfunktionen</string>
<string name="settings_core_only_summary">Aktiviert lediglich die Kernfunktionen, Module werden nicht geladen. MagiskSU, Magisk Hide und Systemless hosts bleiben weiterhin aktiv</string>
<string name="settings_magiskhide_summary">Versteckt Magisk vor diversen Entdeckungsmethoden</string>
<string name="settings_hosts_title">Systemlose hosts-Datei</string>
<string name="settings_hosts_title">Systemlose Hosts-Datei</string>
<string name="settings_hosts_summary">Systemlose Unterstützung für Werbeblocker</string>
<string name="settings_su_app_adb">Apps und ADB</string>
@@ -160,6 +165,7 @@
<string name="settings_su_reauth_summary">Superuser-Zugriff nach App-Aktualisierung erneut abfragen</string>
<string name="settings_su_fingerprint_title">Aktiviere Authentifizierung durch Fingerabdruck</string>
<string name="settings_su_fingerprint_summary">Fingerabdrucksensor benutzen um Superuser-Anfragen zu erlauben</string>
<string name="auth_fingerprint">Authentifiziere Fingerabdruck</string>
<string name="multiuser_mode">Mehrbenutzermodus</string>
<string name="settings_owner_only">Nur der Gerätebesitzer</string>
@@ -168,15 +174,15 @@
<string name="owner_only_summary">Nur der Besitzer hat Root-Zugriff</string>
<string name="owner_manage_summary">Nur der Besitzer verwaltet den Root-Zugriff und erhält Zugriffs-Anfragen</string>
<string name="user_indepenent_summary">Jeder Nutzer hat seine eingenen Root-Regeln</string>
<string name="multiuser_hint_owner_request">Eine Anfrage wurde an den Gerätebesitzer gesendet. Bitte wechsle zum Bersitzerkonto und gewähre die Rechte</string>
<string name="multiuser_hint_owner_request">Eine Anfrage wurde an den Gerätebesitzer gesendet. Bitte wechsel zum Bersitzerkonto und gewähre die Rechte.</string>
<string name="mount_namespace_mode">Namensraum-Modus</string>
<string name="settings_ns_global">Globaler Namensraum</string>
<string name="settings_ns_requester">Geerbter Namensraum</string>
<string name="settings_ns_isolate">Isolierter Namensraum</string>
<string name="global_summary">Alle Root-Sitzungen benutzen den global angelegten Namensraum</string>
<string name="requester_summary">Root-Sitzungen erben den Namensraum des Abfragenden</string>
<string name="isolate_summary">Jede Root-Sitzung hat ihren isolierten Namensraum</string>
<string name="mount_namespace_mode">Mount-Namespace-Modus</string>
<string name="settings_ns_global">Globaler Namespace</string>
<string name="settings_ns_requester">Geerbter Namespace</string>
<string name="settings_ns_isolate">Isolierter Namespace</string>
<string name="global_summary">Alle Root-Sitzungen benutzen den global angelegten Namespace</string>
<string name="requester_summary">Root-Sitzungen erben den Namespace des Abfragenden</string>
<string name="isolate_summary">Jede Root-Sitzung hat ihren isolierten Namespace</string>
<string name="android_o_not_support">Android 8.0+ wird nicht unterstützt</string>
<string name="disable_fingerprint">Keine Fingerabdrücke gespeichert oder keine Geräteunterstützung</string>

Some files were not shown because too many files have changed in this diff Show More