Compare commits

..

72 Commits

Author SHA1 Message Date
topjohnwu
c6bf7bb9cd Bump version 2017-02-06 08:34:55 +08:00
linar10
2a84d92cbf Update strings.xml 2017-02-06 08:32:20 +08:00
linar10
62de36b0da Update strings - pl last changes 2017-02-06 08:32:20 +08:00
c727
03a9aaeff7 strings-de latest changes for release 2017-02-06 08:32:07 +08:00
topjohnwu
45765e292d Final fixes 2017-02-06 08:16:48 +08:00
topjohnwu
6e28a26015 Add uninstall button 2017-02-06 03:20:17 +08:00
topjohnwu
9150bf720d Add info for MagiskHide when not using MagiskSU
Close #63
2017-02-06 03:20:16 +08:00
topjohnwu
845864679c Allow multi lines
Fix #53
2017-02-05 22:05:44 +08:00
topjohnwu
b3b2149ebb Optimize root shell and startups 2017-02-05 22:02:14 +08:00
topjohnwu
0886dca385 string.xml update 2017-02-05 04:46:59 +08:00
c727
53198ba4a7 update for strings-de 2017-02-05 04:42:31 +08:00
killer7mod
a9652ee1fd update strings.xml PT 2017-02-05 04:42:22 +08:00
gh2923
75caf2f01c Update Simplified Chinese Translation 2017-02-05 04:42:04 +08:00
linar10
65bab2666e Update strings.xml -PL 2017-02-05 04:41:54 +08:00
Fabio
6d93ae399a Update italian Translation [1/2] 2017-02-05 04:41:41 +08:00
topjohnwu
7239c2e31a Update to the latest settings 2017-02-05 04:40:52 +08:00
topjohnwu
43b7ef8110 Add disable, change busybox 2017-02-02 19:19:22 +08:00
topjohnwu
99ef0b8cb4 Handle MagiskHide at boot 2017-02-01 23:54:32 +08:00
topjohnwu
0efb4da0ee Several bug fixes
Fix #57
2017-01-31 03:39:24 +08:00
linar10
ed7920d61e Added missing entries for strings-pl 2017-01-30 20:12:53 +08:00
c727
c0379c8e25 update strings-de to "Add Superuser settings" 2017-01-30 20:11:56 +08:00
tonymanou
00a0e64fdd Prefer List/Map/Set as declaring type over their implementations
Unless your are using a method declared in subclasses of an
interface, it is better to use the interface as declaring type.
One advantage of this is that changing used implementation will
be much simpler (you will have less declarations to edit).
2017-01-30 20:11:17 +08:00
tonymanou
0dc60debea Fix warning about use of API limited to support package 2017-01-30 20:11:17 +08:00
tonymanou
c44ae5888c Optimize map operations 2017-01-30 20:11:17 +08:00
topjohnwu
b9495cd1bb Improve static data management 2017-01-30 20:04:49 +08:00
topjohnwu
bfec381933 Improve su requests 2017-01-30 19:27:00 +08:00
topjohnwu
2dddb8df69 Reset menu every transaction 2017-01-30 01:51:55 +08:00
topjohnwu
d30397e9c0 Let users know why blacklist PoGO and AP... 2017-01-30 01:40:51 +08:00
topjohnwu
d9597549fd Prevent excessive su requests 2017-01-30 00:44:33 +08:00
topjohnwu
13512b4146 Add BootReceiver 2017-01-29 16:52:43 +08:00
topjohnwu
49e546919a Update logs 2017-01-29 16:20:41 +08:00
topjohnwu
586015c2ed Fix ButterKnife issue
https://code.google.com/p/android/issues/detail?id=231597
2017-01-29 10:27:06 +08:00
topjohnwu
4a7e067d1a Use support library 2017-01-29 00:20:43 +08:00
topjohnwu
9bc0b7f183 Update settings 2017-01-28 22:02:33 +08:00
topjohnwu
cd4dfc9861 Add Superuser settings 2017-01-28 06:13:07 +08:00
topjohnwu
09bdbc1224 Revert "Read only the first line instead of loading the whole file"
This reverts commit a5b573eaaa.

The file shall always have one single line, no need to create a new method
2017-01-28 01:25:51 +08:00
tonymanou
978b3a64c5 Remove context reference from recyclerview adapter 2017-01-28 01:25:15 +08:00
tonymanou
651547ef20 Fix raw use of generics warnings 2017-01-28 01:25:15 +08:00
tonymanou
b4d95977d0 Remove redundant XML namespaces 2017-01-28 01:25:15 +08:00
tonymanou
5d8bb897db Separate JNI glue from actual C code, move CMakeLists file 2017-01-28 01:25:15 +08:00
tonymanou
84c8ecb372 Slight improvement for the navigation drawer 2017-01-28 01:25:15 +08:00
tonymanou
61abe5b948 Do not close the whole application in case of error 2017-01-28 01:25:15 +08:00
tonymanou
a5b573eaaa Read only the first line instead of loading the whole file 2017-01-28 01:25:15 +08:00
topjohnwu
cbb32f82eb Add Superuser logging UI 2017-01-28 01:13:28 +08:00
topjohnwu
ca9334b2df Add tabs to log fragment 2017-01-27 03:43:37 +08:00
topjohnwu
959ed7f866 Implement logging and bug fixes 2017-01-27 01:02:40 +08:00
c727
a5c0411be0 update strings-de 2017-01-26 14:28:27 +08:00
linar10
32e1303742 Add Polish translate 2017-01-26 14:28:04 +08:00
topjohnwu
7263b6fe89 Handle bootblock detect failure cases 2017-01-26 14:25:12 +08:00
topjohnwu
46a4070f84 Prevent shell response crashes 2017-01-26 13:46:54 +08:00
topjohnwu
c3c155a1ed Improved settings 2017-01-26 04:17:51 +08:00
topjohnwu
b067105660 Fix bug where no info is available 2017-01-26 03:45:05 +08:00
topjohnwu
15ca18848e Add su revoke 2017-01-26 03:30:12 +08:00
topjohnwu
67c9e2ead6 Add Superuser management UI 2017-01-26 01:13:23 +08:00
topjohnwu
3681177be4 Rename fragment layouts 2017-01-25 17:07:23 +08:00
topjohnwu
6eb814ef0b Fix some small issues 2017-01-25 16:45:55 +08:00
topjohnwu
bcc695234c Seperate Configs 2017-01-25 13:17:33 +08:00
topjohnwu
ad16a6fc1b Project restructure 2017-01-25 04:33:22 +08:00
topjohnwu
478b7eeb65 Stop countdown when user reacts 2017-01-25 02:16:36 +08:00
topjohnwu
151a153dc9 Fix toasts and timeouts 2017-01-25 01:23:41 +08:00
topjohnwu
ad131854ca Update request popup UI 2017-01-25 01:01:12 +08:00
topjohnwu
0bd0eb9e59 Magisk Manager is now a SU client
1. Add request popup
2. Add su request notifications
3. Add su database helpers
2017-01-24 14:19:28 +08:00
c727
cf16fd0104 update strings-de for Magisk Manager 3.1 2017-01-15 02:37:58 +08:00
tonymanou
21b00ac6ca Use try-with-resources in some places 2017-01-15 02:37:40 +08:00
tonymanou
57e6f3080c Fix generic type 2017-01-15 02:37:40 +08:00
tonymanou
89744100ce Remove unnecessary Butterknife binding in adapters 2017-01-15 02:37:40 +08:00
tonymanou
a718f9bbfd Unbind Butterknife-injected views in fragment's onDestroyView() 2017-01-15 02:37:40 +08:00
tonymanou
e81bc4f044 Clean up main activity code
No need to catch IllegalStateException as we display the fragment from
onCreate() without delay.
2017-01-15 02:37:40 +08:00
tonymanou
4dbacd79ae Matching event [un]registering, call super at the end of onPause/onDestroy
Event unregistered in onDestroy() should be registered in onCreate() to
avoid being registered multiple times.
2017-01-15 02:37:40 +08:00
tonymanou
ae74d54451 Events should be final in order to work 2017-01-15 02:37:40 +08:00
tonymanou
dc316c5669 Set fragment title and [un]register callbacks in onStart/onStop
onStart() is called when the fragment is made visible, whereas onPause()
is called when the fragment looses focus e.g. if a dialog is shown.
Thus:
- there is no need to set the activity's title everytime the fragment
regains focus,
- it is better to listen to event tasks and refresh the state of the UI
while the fragment is actually visible, listening to events until the
fragment is destroyed is useless: if an event is received between
onStop() and onDestroy(), there will be some processing but nothing will
be shown because the fragment is no longer visible.
2017-01-15 02:37:40 +08:00
tonymanou
e9f04256c9 setHasOptionsMenu() should be called from fragment's onCreate() 2017-01-15 02:37:40 +08:00
95 changed files with 4222 additions and 1223 deletions

View File

@@ -1,5 +0,0 @@
cmake_minimum_required(VERSION 3.6)
add_library(zipadjust SHARED src/main/jni/zipadjust.c)
find_library(libz z)
find_library(liblog log)
target_link_libraries(zipadjust ${libz} ${liblog})

View File

@@ -8,10 +8,11 @@ android {
applicationId "com.topjohnwu.magisk"
minSdkVersion 21
targetSdkVersion 25
versionCode 12
versionName "3.1"
versionCode 20
versionName "4.0"
jackOptions {
enabled true
jackInProcess true
}
ndk {
moduleName 'zipadjust'
@@ -19,10 +20,6 @@ android {
}
}
compileOptions {
incremental false
}
buildTypes {
release {
minifyEnabled true
@@ -34,11 +31,11 @@ android {
targetCompatibility JavaVersion.VERSION_1_8
}
dexOptions {
preDexLibraries = false
preDexLibraries = true
}
externalNativeBuild {
cmake {
path 'CMakeLists.txt'
path 'src/main/jni/CMakeLists.txt'
}
}
}
@@ -49,16 +46,20 @@ repositories {
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:recyclerview-v7:25.1.0'
compile 'com.android.support:cardview-v7:25.1.0'
compile 'com.android.support:design:25.1.0'
compile 'com.jakewharton:butterknife:8.4.0'
compile 'com.android.support:recyclerview-v7:25.1.1'
compile 'com.android.support:cardview-v7:25.1.1'
compile 'com.android.support:design:25.1.1'
compile 'com.android.support:support-v4:25.1.1'
compile 'com.android.support:support-v13:25.1.1'
compile 'com.jakewharton:butterknife:8.5.1'
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.github.clans:fab:1.6.4'
compile 'com.thoughtbot:expandablerecyclerview:1.4'
compile 'com.madgag.spongycastle:core:1.54.0.0'
compile 'com.madgag.spongycastle:prov:1.54.0.0'
compile 'com.madgag.spongycastle:pkix:1.54.0.0'
compile 'com.madgag.spongycastle:pg:1.54.0.0'
compile 'com.google.android.gms:play-services-safetynet:9.0.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.4.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'
}

View File

@@ -29,7 +29,7 @@
# Application classes that will be serialized/deserialized over Gson
-keep class com.topjohnwu.magisk.module.** { *; }
-keep class com.topjohnwu.magisk.utils.ModuleHelper$ValueSortedMap { *; }
-keep class com.topjohnwu.magisk.module.ModuleHelper$ValueSortedMap { *; }
# Prevent proguard from stripping interface information from TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)

View File

@@ -8,9 +8,6 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<application
android:allowBackup="true"
@@ -41,6 +38,26 @@
<activity
android:name=".SettingsActivity"
android:theme="@style/AppTheme.Transparent" />
<activity
android:name=".superuser.RequestActivity"
android:excludeFromRecents="true"
android:launchMode="singleTask"
android:taskAffinity="internal.superuser"
android:theme="@android:style/Theme.NoDisplay" />
<activity
android:name=".superuser.SuRequestActivity"
android:excludeFromRecents="true"
android:taskAffinity="internal.superuser"
android:theme="@style/SuRequest" />
<receiver
android:name=".superuser.SuReceiver" />
<receiver android:name=".receivers.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<provider
android:name="android.support.v4.content.FileProvider"

View File

@@ -0,0 +1,150 @@
#!/system/bin/sh
[ -z $BOOTMODE ] && BOOTMODE=false
TMPDIR=/tmp
($BOOTMODE) && TMPDIR=/dev/tmp
BINDIR=/data/magisk
CHROMEDIR=$BINDIR/chromeos
NEWBOOT=$TMPDIR/boottmp/new-boot.img
UNPACKDIR=$TMPDIR/boottmp/bootunpack
RAMDISK=$TMPDIR/boottmp/ramdisk
SYSTEMLIB=/system/lib
[ -d /system/lib64 ] && SYSTEMLIB=/system/lib64
ui_print() {
echo "$1"
}
grep_prop() {
REGEX="s/^$1=//p"
shift
FILES=$@
if [ -z "$FILES" ]; then
FILES='/system/build.prop'
fi
cat $FILES 2>/dev/null | sed -n $REGEX | head -n 1
}
find_boot_image() {
if [ -z "$BOOTIMAGE" ]; then
for PARTITION in kern-a KERN-A android_boot ANDROID_BOOT kernel KERNEL boot BOOT lnx LNX; do
BOOTIMAGE=`readlink /dev/block/by-name/$PARTITION || readlink /dev/block/platform/*/by-name/$PARTITION || readlink /dev/block/platform/*/*/by-name/$PARTITION`
if [ ! -z "$BOOTIMAGE" ]; then break; fi
done
fi
if [ -z "$BOOTIMAGE" ]; then
FSTAB="/etc/recovery.fstab"
[ ! -f "$FSTAB" ] && FSTAB="/etc/recovery.fstab.bak"
[ -f "$FSTAB" ] && BOOTIMAGE=`grep -E '\b/boot\b' "$FSTAB" | grep -oE '/dev/[a-zA-Z0-9_./-]*'`
fi
}
unpack_boot() {
rm -rf $UNPACKDIR $RAMDISK 2>/dev/null
mkdir -p $UNPACKDIR
mkdir -p $RAMDISK
cd $UNPACKDIR
LD_LIBRARY_PATH=$SYSTEMLIB $BINDIR/bootimgtools --extract $1
cd $RAMDISK
$BINDIR/busybox gunzip -c < $UNPACKDIR/ramdisk.gz | cpio -i
}
repack_boot() {
cd $RAMDISK
find . | cpio -o -H newc 2>/dev/null | gzip -9 > $UNPACKDIR/ramdisk.gz
cd $UNPACKDIR
LD_LIBRARY_PATH=$SYSTEMLIB $BINDIR/bootimgtools --repack $BOOTIMAGE
if [ -f chromeos ]; then
echo " " > config
echo " " > bootloader
LD_LIBRARY_PATH=$SYSTEMLIB $CHROMEDIR/futility vbutil_kernel --pack new-boot.img.signed --keyblock $CHROMEDIR/kernel.keyblock --signprivate $CHROMEDIR/kernel_data_key.vbprivk --version 1 --vmlinuz new-boot.img --config config --arch arm --bootloader bootloader --flags 0x1
rm -f new-boot.img
mv new-boot.img.signed new-boot.img
fi
if ($SAMSUNG); then
SAMSUNG_CHECK=$(cat new-boot.img | grep SEANDROIDENFORCE)
if [ $? -ne 0 ]; then
echo -n "SEANDROIDENFORCE" >> new-boot.img
fi
fi
if ($LGE_G); then
# Prevent secure boot error on LG G2/G3.
# Just for know, It's a pattern which bootloader verifies at boot. Thanks to LG hackers.
echo -n -e "\x41\xa9\xe4\x67\x74\x4d\x1d\x1b\xa4\x29\xf2\xec\xea\x65\x52\x79" >> new-boot.img
fi
mv new-boot.img $NEWBOOT
}
# Set permissions
chmod -R 755 $CHROMEDIR/futility $BINDIR
# Find the boot image
find_boot_image
if [ -z "$BOOTIMAGE" ]; then
ui_print "! Unable to detect boot image"
exit 1
fi
ui_print "- Found Boot Image: $BOOTIMAGE"
# Detect special vendors
SAMSUNG=false
SAMSUNG_CHECK=$(cat /system/build.prop | grep "ro.build.fingerprint=" | grep -i "samsung")
if [ $? -eq 0 ]; then
SAMSUNG=true
fi
LGE_G=false
RBRAND=$(grep_prop ro.product.brand)
RMODEL=$(grep_prop ro.product.device)
if [ "$RBRAND" = "lge" ] || [ "$RBRAND" = "LGE" ]; then
if [ "$RMODEL" = "*D80*" ] ||
[ "$RMODEL" = "*S98*" ] ||
[ "$RMODEL" = "*D85*" ] ||
[ "$RMODEL" = "*F40*" ]; then
LGE_G=true
ui_print "! Bump device detected"
fi
fi
# First unpack the boot image
unpack_boot $BOOTIMAGE
SUPERSU=false
[ -f sbin/launch_daemonsu.sh ] && SUPERSU=true
if ($SUPERSU); then
ui_print "- SuperSU patched image detected"
rm -f magisk sbin/init.magisk.rc sbin/magic_mask.sh
repack_boot
else
if [ -f /data/stock_boot.img ]; then
ui_print "- Boot image backup found!"
NEWBOOT=/data/stock_boot.img
else
ui_print "! Boot image backup unavailable"
if [ -d ".backup" ]; then
ui_print "- Restoring ramdisk with backup"
cp -af .backup/. .
fi
rm -f magisk sbin/init.magisk.rc sbin/magic_mask.sh
repack_boot
fi
fi
chmod 644 $NEWBOOT
ui_print "- Flashing stock/reverted image"
[ ! -L "$BOOTIMAGE" ] && dd if=/dev/zero of=$BOOTIMAGE bs=4096 2>/dev/null
dd if=$NEWBOOT of=$BOOTIMAGE bs=4096
ui_print "- Removing Magisk files"
rm -rf /cache/magisk.log /cache/last_magisk.log /cache/magiskhide.log /cache/.disable_magisk \
/cache/magisk /cache/magisk_merge /cache/magisk_mount /cache/unblock /cache/magisk_uninstaller.sh \
/data/Magisk.apk /data/magisk.apk /data/magisk.img /data/magisk_merge.img \
/data/busybox /data/magisk /data/custom_ramdisk_patch.sh 2>/dev/null
($BOOTMODE) && reboot

View File

@@ -28,9 +28,10 @@ import butterknife.ButterKnife;
public class AboutActivity extends AppCompatActivity {
private static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/MagiskManager";
private static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382";
private static final String DONATION_URL = "http://topjohnwu.github.io/donate";
private static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382";
private static final String SOURCE_CODE_URL = "https://github.com/topjohnwu/MagiskManager";
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.app_version_info) AboutCardRow appVersionInfo;
@BindView(R.id.app_changelog) AboutCardRow appChangelog;
@@ -45,7 +46,7 @@ public class AboutActivity extends AppCompatActivity {
super.onCreate(savedInstanceState);
String theme = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("theme", "");
Logger.dev("AboutActivity: Theme is " + theme);
if (Utils.isDarkTheme) {
if (Global.Configs.isDarkTheme) {
setTheme(R.style.AppTheme_dh);
}
setContentView(R.layout.activity_about);
@@ -63,13 +64,11 @@ public class AboutActivity extends AppCompatActivity {
appVersionInfo.setSummary(BuildConfig.VERSION_NAME);
String changes = null;
try {
InputStream is = getAssets().open("changelog.html");
try (InputStream is = getAssets().open("changelog.html")) {
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
is.close();
changes = new String(buffer);
} catch (IOException ignored) {

View File

@@ -0,0 +1,133 @@
package com.topjohnwu.magisk;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;
import android.preference.PreferenceManager;
import android.util.SparseArray;
import com.topjohnwu.magisk.module.Module;
import com.topjohnwu.magisk.module.Repo;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ValueSortedMap;
import java.io.File;
import java.util.List;
public class Global {
public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk";
public static class Info {
public static double magiskVersion;
public static String magiskVersionString = "(none)";
public static double remoteMagiskVersion = -1;
public static String magiskLink;
public static String releaseNoteLink;
public static int SNCheckResult = -1;
public static String bootBlock = null;
public static boolean isSuClient = false;
public static String suVersion = null;
public static boolean disabled = false;
}
public static class Data {
public static ValueSortedMap<String, Repo> repoMap;
public static ValueSortedMap<String, Module> moduleMap;
public static List<String> blockList;
public static List<ApplicationInfo> appList;
public static List<String> magiskHideList;
}
public static class Events {
public static final CallbackHandler.Event blockDetectionDone = new CallbackHandler.Event();
public static final CallbackHandler.Event packageLoadDone = new CallbackHandler.Event();
public static final CallbackHandler.Event reloadMainActivity = new CallbackHandler.Event();
public static final CallbackHandler.Event moduleLoadDone = new CallbackHandler.Event();
public static final CallbackHandler.Event repoLoadDone = new CallbackHandler.Event();
public static final CallbackHandler.Event updateCheckDone = new CallbackHandler.Event();
public static final CallbackHandler.Event safetyNetDone = new CallbackHandler.Event();
public static SparseArray<CallbackHandler.Event> uidMap = new SparseArray<>();
}
public static class Configs {
public static boolean isDarkTheme;
public static boolean shellLogging;
public static boolean devLogging;
public static boolean magiskHide;
public static int suRequestTimeout;
public static int suLogTimeout = 14;
public static int suAccessState;
public static int suResponseType;
public static int suNotificationType;
}
public static void init(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
Configs.isDarkTheme = prefs.getBoolean("dark_theme", false);
Configs.devLogging = prefs.getBoolean("developer_logging", false);
Configs.shellLogging = prefs.getBoolean("shell_logging", false);
Configs.magiskHide = prefs.getBoolean("magiskhide", false);
updateMagiskInfo();
initSuAccess();
initSuConfigs(context);
// Initialize prefs
prefs.edit()
.putBoolean("dark_theme", Configs.isDarkTheme)
.putBoolean("magiskhide", Configs.magiskHide)
.putBoolean("busybox", Utils.commandExists("busybox"))
.putBoolean("hosts", new File("/magisk/.core/hosts").exists())
.putBoolean("disable", Utils.itemExist(MAGISK_DISABLE_FILE))
.putString("su_request_timeout", String.valueOf(Configs.suRequestTimeout))
.putString("su_auto_response", String.valueOf(Configs.suResponseType))
.putString("su_notification", String.valueOf(Configs.suNotificationType))
.putString("su_access", String.valueOf(Configs.suAccessState))
.apply();
}
public static void initSuConfigs(Context context) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
Configs.suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10);
Configs.suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0);
Configs.suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1);
}
public static void initSuAccess() {
List<String> ret = Shell.sh("su -v");
if (Utils.isValidShellResponse(ret)) {
Info.suVersion = ret.get(0);
Info.isSuClient = Info.suVersion.toUpperCase().contains("MAGISK");
}
if (Info.isSuClient) {
ret = Shell.sh("getprop persist.sys.root_access");
if (Utils.isValidShellResponse(ret))
Configs.suAccessState = Integer.parseInt(ret.get(0));
else {
Shell.su(true, "setprop persist.sys.root_access 3");
Configs.suAccessState = 3;
}
}
}
public static void updateMagiskInfo() {
List<String> ret = Shell.sh("getprop magisk.version");
if (!Utils.isValidShellResponse(ret)) {
Info.magiskVersion = -1;
} else {
try {
Info.magiskVersionString = ret.get(0);
Info.magiskVersion = Double.parseDouble(ret.get(0));
} catch (NumberFormatException e) {
// Custom version don't need to receive updates
Info.magiskVersion = Double.POSITIVE_INFINITY;
}
}
ret = Shell.sh("getprop ro.magisk.disable");
try {
Info.disabled = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
} catch (NumberFormatException e) {
Info.disabled = false;
}
}
}

View File

@@ -1,10 +1,12 @@
package com.topjohnwu.magisk;
import android.app.Fragment;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.CardView;
import android.view.LayoutInflater;
import android.view.View;
@@ -17,60 +19,113 @@ import android.widget.TextView;
import com.topjohnwu.magisk.receivers.MagiskDlReceiver;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class InstallFragment extends Fragment implements CallbackHandler.EventListener {
public static final CallbackHandler.Event blockDetectionDone = new CallbackHandler.Event();
public static List<String> blockList;
public static String bootBlock = null;
private static final String UNINSTALLER = "magisk_uninstaller.sh";
private Unbinder unbinder;
@BindView(R.id.current_version_title) TextView currentVersionTitle;
@BindView(R.id.install_title) TextView installTitle;
@BindView(R.id.block_spinner) Spinner spinner;
@BindView(R.id.detect_bootimage) Button detectButton;
@BindView(R.id.flash_button) CardView flashButton;
@BindView(R.id.uninstall_button) CardView uninstallButton;
@BindView(R.id.keep_force_enc) CheckBox keepEncChkbox;
@BindView(R.id.keep_verity) CheckBox keepVerityChkbox;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.install_fragment, container, false);
ButterKnife.bind(this, v);
View v = inflater.inflate(R.layout.fragment_install, container, false);
unbinder = ButterKnife.bind(this, v);
detectButton.setOnClickListener(v1 -> toAutoDetect());
currentVersionTitle.setText(getString(R.string.current_magisk_title, StatusFragment.magiskVersionString));
installTitle.setText(getString(R.string.install_magisk_title, StatusFragment.remoteMagiskVersion));
currentVersionTitle.setText(getString(R.string.current_magisk_title, Global.Info.magiskVersionString));
installTitle.setText(getString(R.string.install_magisk_title, Global.Info.remoteMagiskVersion));
flashButton.setOnClickListener(v1 -> {
String bootImage = bootBlock;
if (bootImage == null) {
bootImage = blockList.get(spinner.getSelectedItemPosition() - 1);
String bootImage;
if (Global.Info.bootBlock != null) {
if (spinner.getSelectedItemPosition() > 0)
bootImage = Global.Data.blockList.get(spinner.getSelectedItemPosition() - 1);
else
bootImage = Global.Info.bootBlock;
} else {
bootImage = Global.Data.blockList.get(spinner.getSelectedItemPosition());
}
String filename = "Magisk-v" + StatusFragment.remoteMagiskVersion + ".zip";
String finalBootImage = bootImage;
String filename = "Magisk-v" + Global.Info.remoteMagiskVersion + ".zip";
Utils.getAlertDialogBuilder(getActivity())
.setTitle(getString(R.string.repo_install_title, getString(R.string.magisk)))
.setMessage(getString(R.string.repo_install_msg, filename))
.setCancelable(true)
.setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive(
getActivity(),
new MagiskDlReceiver(finalBootImage, keepEncChkbox.isChecked(), keepVerityChkbox.isChecked()),
StatusFragment.magiskLink,
new MagiskDlReceiver(bootImage, keepEncChkbox.isChecked(), keepVerityChkbox.isChecked()),
Global.Info.magiskLink,
Utils.getLegalFilename(filename)))
.setNeutralButton(R.string.check_release_notes, (dialog, which) -> {
getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(StatusFragment.releaseNoteLink)));
getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Global.Info.releaseNoteLink)));
})
.setNegativeButton(R.string.no_thanks, null)
.show();
});
if (blockDetectionDone.isTriggered) {
if (Global.Info.magiskVersion < 10.3) {
uninstallButton.setVisibility(View.GONE);
} else {
uninstallButton.setOnClickListener(vi -> {
Utils.getAlertDialogBuilder(getActivity())
.setTitle("Uninstall Magisk")
.setMessage("This will remove all modules, MagiskSU, and potentially re-encrypt your device\nAre you sure to process?")
.setPositiveButton(R.string.yes, (dialogInterface, i) -> {
try {
InputStream in = getActivity().getAssets().open(UNINSTALLER);
File uninstaller = new File(getActivity().getCacheDir().getAbsolutePath() + "/" + UNINSTALLER);
FileOutputStream out = new FileOutputStream(uninstaller);
byte[] bytes = new byte[1024];
int read;
while ((read = in.read(bytes)) != -1)
out.write(bytes, 0, read);
in.close();
out.close();
ProgressDialog progress = new ProgressDialog(getActivity());
progress.setTitle(R.string.reboot);
progress.show();
new CountDownTimer(5000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
progress.setMessage(getString(R.string.reboot_countdown, millisUntilFinished / 1000));
}
@Override
public void onFinish() {
progress.setMessage(getString(R.string.reboot_countdown, 0));
Shell.su(true, "cp -af " + uninstaller + " /cache/" + UNINSTALLER,
"reboot");
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
})
.setNegativeButton(R.string.no_thanks, null)
.show();
});
}
if (Global.Events.blockDetectionDone.isTriggered) {
updateUI();
}
return v;
@@ -82,8 +137,9 @@ public class InstallFragment extends Fragment implements CallbackHandler.EventLi
}
private void updateUI() {
List<String> items = new ArrayList<>(blockList);
items.add(0, getString(R.string.auto_detect, bootBlock));
List<String> items = new ArrayList<>(Global.Data.blockList);
if (Global.Info.bootBlock != null)
items.add(0, getString(R.string.auto_detect, Global.Info.bootBlock));
ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(),
android.R.layout.simple_spinner_item, items);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
@@ -92,21 +148,27 @@ public class InstallFragment extends Fragment implements CallbackHandler.EventLi
}
private void toAutoDetect() {
if (bootBlock != null) {
if (Global.Info.bootBlock != null) {
spinner.setSelection(0);
}
}
@Override
public void onResume() {
super.onResume();
public void onStart() {
super.onStart();
getActivity().setTitle(R.string.install);
CallbackHandler.register(blockDetectionDone, this);
CallbackHandler.register(Global.Events.blockDetectionDone, this);
}
@Override
public void onDestroy() {
super.onDestroy();
CallbackHandler.unRegister(blockDetectionDone, this);
public void onStop() {
CallbackHandler.unRegister(Global.Events.blockDetectionDone, this);
super.onStop();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@@ -1,236 +1,53 @@
package com.topjohnwu.magisk;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Fragment;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
import java.util.List;
import com.topjohnwu.magisk.adapters.TabFragmentAdapter;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class LogFragment extends Fragment {
private static final String MAGISK_LOG = "/cache/magisk.log";
private Unbinder unbinder;
@BindView(R.id.txtLog) TextView txtLog;
@BindView(R.id.svLog) ScrollView svLog;
@BindView(R.id.hsvLog) HorizontalScrollView hsvLog;
@BindView(R.id.progressBar) ProgressBar progressBar;
private MenuItem mClickedMenuItem;
@BindView(R.id.container) ViewPager viewPager;
@BindView(R.id.tab) TabLayout tab;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.log_fragment, container, false);
ButterKnife.bind(this, view);
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_log, container, false);
unbinder = ButterKnife.bind(this, v);
txtLog.setTextIsSelectable(true);
TabFragmentAdapter adapter = new TabFragmentAdapter(getChildFragmentManager());
new LogManager().read();
adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk));
return view;
if (Global.Info.isSuClient) {
adapter.addTab(new SuLogFragment(), getString(R.string.superuser));
tab.setupWithViewPager(viewPager);
tab.setVisibility(View.VISIBLE);
}
viewPager.setAdapter(adapter);
return v;
}
@Override
public void onResume() {
super.onResume();
setHasOptionsMenu(true);
new LogManager().read();
getActivity().setTitle(R.string.log);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_log, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
mClickedMenuItem = item;
switch (item.getItemId()) {
case R.id.menu_refresh:
new LogManager().read();
return true;
case R.id.menu_send:
new LogManager().send();
return true;
case R.id.menu_save:
new LogManager().save();
return true;
case R.id.menu_clear:
new LogManager().clear();
return true;
default:
return true;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (mClickedMenuItem != null) {
new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500);
}
} else {
Snackbar.make(txtLog, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
}
}
}
public class LogManager extends Async.RootTask<Object, Void, Object> {
int mode;
File targetFile;
@SuppressLint("DefaultLocale")
@Override
protected Object doInBackground(Object... params) {
mode = (int) params[0];
switch (mode) {
case 0:
List<String> logList = Utils.readFile(MAGISK_LOG);
StringBuilder llog = new StringBuilder(15 * 10 * 1024);
for (String s : logList) {
llog.append(s).append("\n");
}
return llog.toString();
case 1:
Shell.su("echo > " + MAGISK_LOG);
Snackbar.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
return "";
case 2:
case 3:
if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
return false;
}
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
return false;
Calendar now = Calendar.getInstance();
String filename = String.format(
"magisk_%s_%04d%02d%02d_%02d%02d%02d.log", "error",
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
targetFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/MagiskManager/" + filename);
if ((!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs())
|| (targetFile.exists() && !targetFile.delete()))
return false;
List<String> in = Utils.readFile(MAGISK_LOG);
try {
FileWriter out = new FileWriter(targetFile);
for (String line : in) {
out.write(line + "\n");
}
out.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
return null;
}
@Override
protected void onPostExecute(Object o) {
boolean bool;
String llog;
switch (mode) {
case 0:
case 1:
llog = (String) o;
progressBar.setVisibility(View.GONE);
if (llog.length() == 0)
txtLog.setText(R.string.log_is_empty);
else
txtLog.setText(llog);
svLog.post(() -> svLog.scrollTo(0, txtLog.getHeight()));
hsvLog.post(() -> hsvLog.scrollTo(0, 0));
break;
case 2:
bool = (boolean) o;
if (bool)
Toast.makeText(getActivity(), targetFile.toString(), Toast.LENGTH_LONG).show();
else
Toast.makeText(getActivity(), getString(R.string.logs_save_failed), Toast.LENGTH_LONG).show();
break;
case 3:
bool = (boolean) o;
if (bool) {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(targetFile));
sendIntent.setType("application/html");
startActivity(Intent.createChooser(sendIntent, getResources().getString(R.string.menuSend)));
} else {
Toast.makeText(getActivity(), getString(R.string.logs_save_failed), Toast.LENGTH_LONG).show();
}
}
}
public void read() {
exec(0);
}
public void clear() {
exec(1);
}
public void save() {
exec(2);
}
public void send() {
exec(3);
}
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@@ -1,9 +1,9 @@
package com.topjohnwu.magisk;
import android.app.Fragment;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
@@ -20,37 +20,32 @@ import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Logger;
import java.util.Arrays;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class MagiskHideFragment extends Fragment implements CallbackHandler.EventListener {
private Unbinder unbinder;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
// Don't show in list...
public static final List<String> BLACKLIST = Arrays.asList(
"android",
"com.topjohnwu.magisk",
"com.google.android.gms",
"com.google.android.apps.walletnfcrel",
"com.nianticlabs.pokemongo"
);
public static CallbackHandler.Event packageLoadDone = new CallbackHandler.Event();
private ApplicationAdapter appAdapter;
private SearchView.OnQueryTextListener searchListener;
private String lastFilter;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.magisk_hide_fragment, container, false);
ButterKnife.bind(this, view);
View view = inflater.inflate(R.layout.fragment_magisk_hide, container, false);
unbinder = ButterKnife.bind(this, view);
PackageManager packageManager = getActivity().getPackageManager();
@@ -87,27 +82,31 @@ public class MagiskHideFragment extends Fragment implements CallbackHandler.Even
}
@Override
public void onResume() {
super.onResume();
setHasOptionsMenu(true);
public void onStart() {
super.onStart();
getActivity().setTitle(R.string.magiskhide);
CallbackHandler.register(packageLoadDone, this);
if (packageLoadDone.isTriggered) {
onTrigger(packageLoadDone);
CallbackHandler.register(Global.Events.packageLoadDone, this);
if (Global.Events.packageLoadDone.isTriggered) {
onTrigger(Global.Events.packageLoadDone);
}
}
@Override
public void onPause() {
super.onPause();
CallbackHandler.unRegister(packageLoadDone, this);
public void onStop() {
CallbackHandler.unRegister(Global.Events.packageLoadDone, this);
super.onStop();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public void onTrigger(CallbackHandler.Event event) {
Logger.dev("MagiskHideFragment: UI refresh");
Async.LoadApps.Result result = (Async.LoadApps.Result) event.getResult();
appAdapter.setLists(result.listApps, result.hideList);
appAdapter.setLists(Global.Data.appList, Global.Data.magiskHideList);
mSwipeRefreshLayout.setRefreshing(false);
if (!TextUtils.isEmpty(lastFilter)) {
appAdapter.filter(lastFilter);

View File

@@ -0,0 +1,237 @@
package com.topjohnwu.magisk;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.HorizontalScrollView;
import android.widget.ProgressBar;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Calendar;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class MagiskLogFragment extends Fragment {
private static final String MAGISK_LOG = "/cache/magisk.log";
private Unbinder unbinder;
@BindView(R.id.txtLog) TextView txtLog;
@BindView(R.id.svLog) ScrollView svLog;
@BindView(R.id.hsvLog) HorizontalScrollView hsvLog;
@BindView(R.id.progressBar) ProgressBar progressBar;
private MenuItem mClickedMenuItem;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Nullable
@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);
txtLog.setTextIsSelectable(true);
new LogManager().read();
return view;
}
@Override
public void onStart() {
super.onStart();
getActivity().setTitle(R.string.log);
}
@Override
public void onResume() {
super.onResume();
new LogManager().read();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_log, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
mClickedMenuItem = item;
switch (item.getItemId()) {
case R.id.menu_refresh:
new LogManager().read();
return true;
case R.id.menu_save:
new LogManager().save();
return true;
case R.id.menu_clear:
new LogManager().clear();
return true;
default:
return true;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 0) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
if (mClickedMenuItem != null) {
new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500);
}
} else {
Snackbar.make(txtLog, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
}
}
}
public class LogManager extends Async.RootTask<Object, Void, Object> {
int mode;
File targetFile;
@SuppressLint("DefaultLocale")
@Override
protected Object doInBackground(Object... params) {
mode = (int) params[0];
switch (mode) {
case 0:
List<String> logList = Utils.readFile(MAGISK_LOG);
if (Utils.isValidShellResponse(logList)) {
StringBuilder llog = new StringBuilder(15 * 10 * 1024);
for (String s : logList) {
llog.append(s).append("\n");
}
return llog.toString();
}
return "";
case 1:
Shell.su("echo > " + MAGISK_LOG);
Snackbar.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
return "";
case 2:
if (ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 0);
}
return false;
}
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
return false;
Calendar now = Calendar.getInstance();
String filename = String.format(
"magisk_%s_%04d%02d%02d_%02d%02d%02d.log", "error",
now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1,
now.get(Calendar.DAY_OF_MONTH), now.get(Calendar.HOUR_OF_DAY),
now.get(Calendar.MINUTE), now.get(Calendar.SECOND));
targetFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/MagiskManager/" + filename);
if ((!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs())
|| (targetFile.exists() && !targetFile.delete()))
return false;
List<String> in = Utils.readFile(MAGISK_LOG);
if (Utils.isValidShellResponse(in)) {
try (FileWriter out = new FileWriter(targetFile)) {
for (String line : in)
out.write(line + "\n");
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
return false;
}
return null;
}
@Override
protected void onPostExecute(Object o) {
if (o == null) return;
boolean bool;
String llog;
switch (mode) {
case 0:
case 1:
llog = (String) o;
progressBar.setVisibility(View.GONE);
if (TextUtils.isEmpty(llog))
txtLog.setText(R.string.log_is_empty);
else
txtLog.setText(llog);
svLog.post(() -> svLog.scrollTo(0, txtLog.getHeight()));
hsvLog.post(() -> hsvLog.scrollTo(0, 0));
break;
case 2:
bool = (boolean) o;
if (bool)
Toast.makeText(getActivity(), targetFile.toString(), Toast.LENGTH_LONG).show();
else
Toast.makeText(getActivity(), getString(R.string.logs_save_failed), Toast.LENGTH_LONG).show();
break;
}
}
public void read() {
exec(0);
}
public void clear() {
exec(1);
}
public void save() {
exec(2);
}
}
}

View File

@@ -1,8 +1,6 @@
package com.topjohnwu.magisk;
import android.Manifest;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
@@ -10,11 +8,11 @@ import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.app.ActivityCompat;
import android.support.v4.view.GravityCompat;
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.app.AppCompatActivity;
@@ -25,7 +23,6 @@ import android.view.View;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -33,10 +30,6 @@ import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener, CallbackHandler.EventListener {
private static final String SELECTED_ITEM_ID = "SELECTED_ITEM_ID";
public static CallbackHandler.Event recreate = new CallbackHandler.Event();
private final Handler mDrawerHandler = new Handler();
private SharedPreferences prefs;
@@ -44,15 +37,14 @@ public class MainActivity extends AppCompatActivity
@BindView(R.id.drawer_layout) DrawerLayout drawer;
@BindView(R.id.nav_view) public NavigationView navigationView;
@IdRes
private int mSelectedId = R.id.status;
private float toolbarElevation;
@Override
protected void onCreate(final Bundle savedInstanceState) {
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
if (Utils.isDarkTheme) {
if (Global.Configs.isDarkTheme) {
setTheme(R.style.AppTheme_dh);
}
super.onCreate(savedInstanceState);
@@ -79,133 +71,116 @@ public class MainActivity extends AppCompatActivity
}
};
toolbarElevation = toolbar.getElevation();
drawer.addDrawerListener(toggle);
toggle.syncState();
//noinspection ResourceType
mSelectedId = savedInstanceState == null ? mSelectedId : savedInstanceState.getInt(SELECTED_ITEM_ID);
navigationView.setCheckedItem(mSelectedId);
if (savedInstanceState == null) {
mDrawerHandler.removeCallbacksAndMessages(null);
mDrawerHandler.postDelayed(() -> navigate(mSelectedId), 250);
}
navigate(R.id.status);
navigationView.setNavigationItemSelectedListener(this);
CallbackHandler.register(Global.Events.reloadMainActivity, this);
}
@Override
protected void onResume() {
super.onResume();
CallbackHandler.register(StatusFragment.updateCheckDone, this);
CallbackHandler.register(recreate, this);
if (StatusFragment.updateCheckDone.isTriggered) {
onTrigger(StatusFragment.updateCheckDone);
}
CallbackHandler.register(Global.Events.updateCheckDone, this);
if (Global.Events.updateCheckDone.isTriggered)
onTrigger(Global.Events.updateCheckDone);
checkHideSection();
}
@Override
protected void onPause() {
CallbackHandler.unRegister(Global.Events.updateCheckDone, this);
super.onPause();
CallbackHandler.unRegister(StatusFragment.updateCheckDone, this);
}
@Override
protected void onDestroy() {
CallbackHandler.unRegister(Global.Events.reloadMainActivity, this);
super.onDestroy();
CallbackHandler.unRegister(recreate, this);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(SELECTED_ITEM_ID, mSelectedId);
}
@Override
public void onBackPressed() {
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
if (drawer.isDrawerOpen(navigationView))
drawer.closeDrawer(navigationView);
else
finish();
}
}
@Override
public boolean onNavigationItemSelected(@NonNull final MenuItem menuItem) {
mSelectedId = menuItem.getItemId();
mDrawerHandler.removeCallbacksAndMessages(null);
mDrawerHandler.postDelayed(() -> navigate(menuItem.getItemId()), 250);
drawer.closeDrawer(GravityCompat.START);
drawer.closeDrawer(navigationView);
return true;
}
@Override
public void onTrigger(CallbackHandler.Event event) {
if (event == StatusFragment.updateCheckDone) {
if (event == Global.Events.updateCheckDone) {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.install).setVisible(StatusFragment.remoteMagiskVersion > 0 &&
menu.findItem(R.id.install).setVisible(Global.Info.remoteMagiskVersion > 0 &&
Shell.rootAccess());
} else if (event == recreate) {
} else if (event == Global.Events.reloadMainActivity) {
recreate();
}
}
private void checkHideSection() {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.magiskhide).setVisible(StatusFragment.magiskVersion >= 8 &&
prefs.getBoolean("magiskhide", false) && Shell.rootAccess());
menu.findItem(R.id.modules).setVisible(StatusFragment.magiskVersion >= 4 &&
Shell.rootAccess());
menu.findItem(R.id.downloads).setVisible(StatusFragment.magiskVersion >= 4 &&
Shell.rootAccess());
menu.findItem(R.id.log).setVisible(Shell.rootAccess());
menu.findItem(R.id.install).setVisible(Shell.rootAccess());
if (Shell.rootAccess()) {
menu.findItem(R.id.magiskhide).setVisible(
Global.Info.magiskVersion >= 8 && prefs.getBoolean("magiskhide", false));
menu.findItem(R.id.modules).setVisible(Global.Info.magiskVersion >= 4);
menu.findItem(R.id.downloads).setVisible(Global.Info.magiskVersion >= 4);
menu.findItem(R.id.log).setVisible(true);
menu.findItem(R.id.superuser).setVisible(Global.Info.isSuClient);
}
}
public void navigate(final int itemId) {
Fragment navFragment = null;
String tag = "";
public void navigate(int itemId) {
switch (itemId) {
case R.id.status:
tag = "status";
navFragment = new StatusFragment();
displayFragment(new StatusFragment(), "status", true);
break;
case R.id.install:
tag = "install";
navFragment = new InstallFragment();
displayFragment(new InstallFragment(), "install", true);
break;
case R.id.superuser:
displayFragment(new SuperuserFragment(), "superuser", true);
break;
case R.id.modules:
tag = "modules";
navFragment = new ModulesFragment();
displayFragment(new ModulesFragment(), "modules", true);
break;
case R.id.downloads:
tag = "downloads";
navFragment = new ReposFragment();
displayFragment(new ReposFragment(), "downloads", true);
break;
case R.id.magiskhide:
tag = "magiskhide";
navFragment = new MagiskHideFragment();
displayFragment(new MagiskHideFragment(), "magiskhide", true);
break;
case R.id.log:
tag = "log";
navFragment = new LogFragment();
displayFragment(new LogFragment(), "log", false);
toolbar.setElevation(0);
break;
case R.id.settings:
startActivity(new Intent(this, SettingsActivity.class));
break;
case R.id.app_about:
startActivity(new Intent(this, AboutActivity.class));
return;
}
if (navFragment != null) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
try {
transaction.replace(R.id.content_frame, navFragment, tag).commit();
} catch (IllegalStateException ignored) {}
break;
}
}
}
private void displayFragment(@NonNull Fragment navFragment, String tag, boolean setElevation) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
supportInvalidateOptionsMenu();
transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
transaction.replace(R.id.content_frame, navFragment, tag).commitNow();
if (setElevation) toolbar.setElevation(toolbarElevation);
}
}

View File

@@ -1,11 +1,11 @@
package com.topjohnwu.magisk;
import android.app.Activity;
import android.app.Fragment;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
@@ -16,26 +16,26 @@ import android.widget.TextView;
import com.github.clans.fab.FloatingActionButton;
import com.topjohnwu.magisk.adapters.ModulesAdapter;
import com.topjohnwu.magisk.module.Module;
import com.topjohnwu.magisk.module.ModuleHelper;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.ModuleHelper;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class ModulesFragment extends Fragment implements CallbackHandler.EventListener {
public static final CallbackHandler.Event moduleLoadDone = new CallbackHandler.Event();
private static final int FETCH_ZIP_CODE = 2;
private Unbinder unbinder;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyTv;
@BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.fab) FloatingActionButton fabio;
private List<Module> listModules = new ArrayList<>();
@@ -43,8 +43,8 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.modules_fragment, container, false);
ButterKnife.bind(this, view);
View view = inflater.inflate(R.layout.fragment_modules, container, false);
unbinder = ButterKnife.bind(this, view);
fabio.setOnClickListener(v -> {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
@@ -69,7 +69,7 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
}
});
if (moduleLoadDone.isTriggered) {
if (Global.Events.moduleLoadDone.isTriggered) {
updateUI();
}
@@ -93,25 +93,31 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
}
@Override
public void onResume() {
super.onResume();
CallbackHandler.register(moduleLoadDone, this);
public void onStart() {
super.onStart();
CallbackHandler.register(Global.Events.moduleLoadDone, this);
getActivity().setTitle(R.string.modules);
}
@Override
public void onDestroy() {
super.onDestroy();
CallbackHandler.unRegister(moduleLoadDone, this);
public void onStop() {
CallbackHandler.unRegister(Global.Events.moduleLoadDone, this);
super.onStop();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
private void updateUI() {
ModuleHelper.getModuleList(listModules);
if (listModules.size() == 0) {
emptyTv.setVisibility(View.VISIBLE);
emptyRv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
emptyTv.setVisibility(View.GONE);
emptyRv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
recyclerView.setAdapter(new ModulesAdapter(listModules));
}

View File

@@ -1,8 +1,8 @@
package com.topjohnwu.magisk;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.view.MenuItemCompat;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.widget.RecyclerView;
@@ -16,24 +16,24 @@ import android.widget.TextView;
import com.topjohnwu.magisk.adapters.ReposAdapter;
import com.topjohnwu.magisk.adapters.SimpleSectionedRecyclerViewAdapter;
import com.topjohnwu.magisk.module.ModuleHelper;
import com.topjohnwu.magisk.module.Repo;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.CallbackHandler;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.ModuleHelper;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class ReposFragment extends Fragment implements CallbackHandler.EventListener {
public static final CallbackHandler.Event repoLoadDone = new CallbackHandler.Event();
private Unbinder unbinder;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyTv;
@BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
private List<Repo> mUpdateRepos = new ArrayList<>();
@@ -47,16 +47,19 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList
private SearchView.OnQueryTextListener searchListener;
@Nullable
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.repos_fragment, container, false);
View view = inflater.inflate(R.layout.fragment_repos, container, false);
unbinder = ButterKnife.bind(this, view);
ButterKnife.bind(this, view);
mSectionedAdapter = new
SimpleSectionedRecyclerViewAdapter(getActivity(), R.layout.section,
mSectionedAdapter = new SimpleSectionedRecyclerViewAdapter(R.layout.section,
R.id.section_text, new ReposAdapter(fUpdateRepos, fInstalledRepos, fOthersRepos));
recyclerView.setAdapter(mSectionedAdapter);
@@ -68,7 +71,7 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList
new Async.LoadRepos(getActivity()).exec();
});
if (repoLoadDone.isTriggered) {
if (Global.Events.repoLoadDone.isTriggered) {
reloadRepos();
updateUI();
}
@@ -104,17 +107,22 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList
}
@Override
public void onResume() {
super.onResume();
setHasOptionsMenu(true);
CallbackHandler.register(repoLoadDone, this);
public void onStart() {
super.onStart();
CallbackHandler.register(Global.Events.repoLoadDone, this);
getActivity().setTitle(R.string.downloads);
}
@Override
public void onDestroy() {
super.onDestroy();
CallbackHandler.unRegister(repoLoadDone, this);
public void onStop() {
CallbackHandler.unRegister(Global.Events.repoLoadDone, this);
super.onStop();
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
private void reloadRepos() {
@@ -129,7 +137,7 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList
private void updateUI() {
if (fUpdateRepos.size() + fInstalledRepos.size() + fOthersRepos.size() == 0) {
emptyTv.setVisibility(View.VISIBLE);
emptyRv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
List<SimpleSectionedRecyclerViewAdapter.Section> sections = new ArrayList<>();
@@ -144,7 +152,7 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList
}
SimpleSectionedRecyclerViewAdapter.Section[] array = sections.toArray(new SimpleSectionedRecyclerViewAdapter.Section[sections.size()]);
mSectionedAdapter.setSections(array);
emptyTv.setVisibility(View.GONE);
emptyRv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
mSwipeRefreshLayout.setRefreshing(false);

View File

@@ -1,22 +1,22 @@
package com.topjohnwu.magisk;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment;
import android.preference.PreferenceManager;
import android.preference.PreferenceScreen;
import android.preference.SwitchPreference;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.WindowManager;
import android.widget.Toast;
import com.topjohnwu.magisk.module.ModuleHelper;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.ModuleHelper;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
@@ -30,9 +30,7 @@ public class SettingsActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String theme = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("theme", "");
Logger.dev("AboutActivity: Theme is " + theme);
if (Utils.isDarkTheme) {
if (Global.Configs.isDarkTheme) {
setTheme(R.style.AppTheme_dh);
}
@@ -74,52 +72,50 @@ public class SettingsActivity extends AppCompatActivity {
public static class SettingsFragment extends PreferenceFragment
implements SharedPreferences.OnSharedPreferenceChangeListener {
private ListPreference themePreference;
private SharedPreferences prefs;
private PreferenceScreen prefScreen;
private ListPreference suAccess, autoRes, suNotification, requestTimeout;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.app_settings);
PreferenceManager.setDefaultValues(getActivity(), R.xml.app_settings, false);
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
prefScreen = getPreferenceScreen();
themePreference = (ListPreference) findPreference("theme");
CheckBoxPreference busyboxPreference = (CheckBoxPreference) findPreference("busybox");
CheckBoxPreference magiskhidePreference = (CheckBoxPreference) findPreference("magiskhide");
CheckBoxPreference hostsPreference = (CheckBoxPreference) findPreference("hosts");
Preference clear = findPreference("clear");
SwitchPreference busybox = (SwitchPreference) findPreference("busybox");
SwitchPreference magiskHide = (SwitchPreference) findPreference("magiskhide");
SwitchPreference hosts = (SwitchPreference) findPreference("hosts");
clear.setOnPreferenceClickListener((pref) -> {
SharedPreferences repoMap = getActivity().getSharedPreferences(ModuleHelper.FILE_KEY, Context.MODE_PRIVATE);
repoMap.edit()
.putString(ModuleHelper.ETAG_KEY, "")
.putInt(ModuleHelper.VERSION_KEY, 0)
.apply();
new Async.LoadRepos(getActivity()).exec();
Toast.makeText(getActivity(), R.string.repo_cache_cleared, Toast.LENGTH_LONG).show();
PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk");
PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser");
suAccess = (ListPreference) findPreference("su_access");
autoRes = (ListPreference) findPreference("su_auto_response");
requestTimeout = (ListPreference) findPreference("su_request_timeout");
suNotification = (ListPreference) findPreference("su_notification");
setSummary();
findPreference("clear").setOnPreferenceClickListener((pref) -> {
ModuleHelper.clearRepoCache(getActivity());
return true;
});
if (Utils.isDarkTheme) {
themePreference.setSummary(R.string.theme_dark);
if (!Shell.rootAccess()) {
prefScreen.removePreference(magiskCategory);
prefScreen.removePreference(suCategory);
} else {
themePreference.setSummary(R.string.theme_default);
}
if (StatusFragment.magiskVersion < 9) {
hostsPreference.setEnabled(false);
busyboxPreference.setEnabled(false);
} else if (StatusFragment.magiskVersion < 8) {
magiskhidePreference.setEnabled(false);
} else if (! Shell.rootAccess()) {
busyboxPreference.setEnabled(false);
magiskhidePreference.setEnabled(false);
hostsPreference.setEnabled(false);
} else {
busyboxPreference.setEnabled(true);
magiskhidePreference.setEnabled(true);
hostsPreference.setEnabled(true);
if (!Global.Info.isSuClient)
prefScreen.removePreference(suCategory);
if (Global.Info.magiskVersion < 11)
prefScreen.removePreference(magiskCategory);
if (Global.Info.disabled) {
busybox.setEnabled(false);
magiskHide.setEnabled(false);
hosts.setEnabled(false);
}
}
}
@@ -130,62 +126,78 @@ public class SettingsActivity extends AppCompatActivity {
}
@Override
public void onDestroy() {
super.onDestroy();
public void onPause() {
super.onPause();
prefs.unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
Logger.dev("Settings: Prefs change " + key);
boolean checked;
boolean enabled;
switch (key) {
case "theme":
String theme = prefs.getString("theme", getString(R.string.theme_default_value));
if (Utils.isDarkTheme != theme.equalsIgnoreCase(getString(R.string.theme_dark_value))) {
Utils.isDarkTheme = !Utils.isDarkTheme;
case "dark_theme":
enabled = prefs.getBoolean("dark_theme", false);
if (Global.Configs.isDarkTheme != enabled) {
Global.Configs.isDarkTheme = enabled;
getActivity().recreate();
MainActivity.recreate.trigger();
Global.Events.reloadMainActivity.trigger();
}
break;
case "magiskhide":
checked = prefs.getBoolean("magiskhide", false);
case "disable":
enabled = prefs.getBoolean("disable", false);
new Async.RootTask<Void, Void, Void>() {
private boolean enable = checked;
private boolean enable = enabled;
@Override
protected Void doInBackground(Void... params) {
protected Void doInBackground(Void... voids) {
if (enable) {
Utils.createFile("/magisk/.core/magiskhide/enable");
Utils.createFile(Global.MAGISK_DISABLE_FILE);
} else {
Utils.removeItem("/magisk/.core/magiskhide/enable");
Utils.removeItem(Global.MAGISK_DISABLE_FILE);
}
return null;
}
}.exec();
Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show();
break;
case "busybox":
checked = prefs.getBoolean("busybox", false);
enabled = prefs.getBoolean("busybox", false);
new Async.RootTask<Void, Void, Void>() {
private boolean enable = checked;
private boolean enable = enabled;
@Override
protected Void doInBackground(Void... params) {
protected Void doInBackground(Void... voids) {
if (enable) {
Utils.createFile("/magisk/.core/busybox/enable");
Shell.su(
"setprop persist.magisk.busybox 1",
"sh /sbin/magic_mask.sh mount_busybox");
} else {
Utils.removeItem("/magisk/.core/busybox/enable");
Shell.su(
"setprop persist.magisk.busybox 0",
"umount /system/xbin");
}
return null;
}
}.exec();
Toast.makeText(getActivity(), R.string.settings_reboot_toast, Toast.LENGTH_LONG).show();
break;
case "magiskhide":
enabled = prefs.getBoolean("magiskhide", false);
if (enabled) {
if (!Global.Info.isSuClient) {
Utils.getAlertDialogBuilder(getActivity())
.setTitle(R.string.no_magisksu_title)
.setMessage(R.string.no_magisksu_msg)
.setPositiveButton(R.string.understand, (dialog, which) -> new Async.MagiskHide().enable())
.setCancelable(false)
.show();
} else new Async.MagiskHide().enable();
} else
new Async.MagiskHide().disable();
break;
case "hosts":
checked = prefs.getBoolean("hosts", false);
enabled = prefs.getBoolean("hosts", false);
new Async.RootTask<Void, Void, Void>() {
private boolean enable = checked;
private boolean enable = enabled;
@Override
protected Void doInBackground(Void... voids) {
if (enable) {
@@ -199,14 +211,38 @@ public class SettingsActivity extends AppCompatActivity {
}
}.exec();
break;
case "su_access":
Global.Configs.suAccessState = Utils.getPrefsInt(prefs, "su_access", 0);
Shell.su("setprop persist.sys.root_access " + Global.Configs.suAccessState);
break;
case "su_request_timeout":
Global.Configs.suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10);
break;
case "su_auto_response":
Global.Configs.suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0);
break;
case "su_notification":
Global.Configs.suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1);
break;
case "developer_logging":
Logger.devLog = prefs.getBoolean("developer_logging", false);
Global.Configs.devLogging = prefs.getBoolean("developer_logging", false);
break;
case "shell_logging":
Logger.logShell = prefs.getBoolean("shell_logging", false);
Global.Configs.shellLogging = prefs.getBoolean("shell_logging", false);
break;
}
setSummary();
}
private void setSummary() {
suAccess.setSummary(getResources()
.getStringArray(R.array.su_access)[Global.Configs.suAccessState]);
autoRes.setSummary(getResources()
.getStringArray(R.array.auto_response)[Global.Configs.suResponseType]);
suNotification.setSummary(getResources()
.getStringArray(R.array.su_notification)[Global.Configs.suNotificationType]);
requestTimeout.setSummary(
getString(R.string.request_timeout_summary, prefs.getString("su_request_timeout", "10")));
}
}

View File

@@ -1,14 +1,10 @@
package com.topjohnwu.magisk;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v7.app.AppCompatActivity;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
public class SplashActivity extends AppCompatActivity {
@@ -16,28 +12,13 @@ public class SplashActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplication());
String theme = prefs.getString("theme", getString(R.string.theme_default_value));
Utils.isDarkTheme = theme.equalsIgnoreCase(getString(R.string.theme_dark_value));
// Init the info and configs and root shell
Global.init(getApplicationContext());
if (Utils.isDarkTheme) {
setTheme(R.style.AppTheme_dh);
}
Logger.devLog = prefs.getBoolean("developer_logging", false);
Logger.logShell = prefs.getBoolean("shell_logging", false);
// Initialize prefs
prefs.edit()
.putBoolean("magiskhide", Utils.itemExist(false, "/magisk/.core/magiskhide/enable"))
.putBoolean("busybox", Utils.commandExists("busybox"))
.putBoolean("hosts", Utils.itemExist(false, "/magisk/.core/hosts"))
.apply();
// Start all async tasks
new Async.GetBootBlocks().exec();
// Now fire all async tasks
new Async.CheckUpdates().exec();
new Async.GetBootBlocks().exec();
new Async.LoadModules() {
@Override
protected void onPostExecute(Void v) {
@@ -47,7 +28,7 @@ public class SplashActivity extends AppCompatActivity {
}.exec();
new Async.LoadApps(getPackageManager()).exec();
// Start main activity
// Preparation done, now start main activity
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
finish();

View File

@@ -1,12 +1,12 @@
package com.topjohnwu.magisk;
import android.app.AlertDialog;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.widget.SwipeRefreshLayout;
import android.view.LayoutInflater;
import android.view.View;
@@ -21,23 +21,16 @@ import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import java.util.List;
import butterknife.BindColor;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class StatusFragment extends Fragment implements CallbackHandler.EventListener {
public static double magiskVersion, remoteMagiskVersion = -1;
public static String magiskVersionString = "(none)", magiskLink, releaseNoteLink;
public static int SNCheckResult = -1;
private static boolean noDialog = false;
public static final CallbackHandler.Event updateCheckDone = new CallbackHandler.Event();
public static final CallbackHandler.Event safetyNetDone = new CallbackHandler.Event();
private Unbinder unbinder;
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
@BindView(R.id.magisk_status_container) View magiskStatusContainer;
@@ -64,17 +57,13 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
@BindColor(android.R.color.transparent) int trans;
int defaultColor;
static {
checkMagiskInfo();
}
private AlertDialog updateMagisk;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.status_fragment, container, false);
ButterKnife.bind(this, v);
View v = inflater.inflate(R.layout.fragment_status, container, false);
unbinder = ButterKnife.bind(this, v);
defaultColor = magiskUpdateText.getCurrentTextColor();
@@ -91,7 +80,7 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
safetyNetStatusText.setText(R.string.safetyNet_check_text);
safetyNetStatusText.setTextColor(defaultColor);
safetyNetDone.isTriggered = false;
Global.Events.safetyNetDone.isTriggered = false;
noDialog = false;
updateUI();
@@ -106,7 +95,7 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
Async.checkSafetyNet(getActivity());
});
if (magiskVersion < 0 && Shell.rootAccess() && !noDialog) {
if (Global.Info.magiskVersion < 0 && Shell.rootAccess() && !noDialog) {
noDialog = true;
Utils.getAlertDialogBuilder(getActivity())
.setTitle(R.string.no_magisk_title)
@@ -131,79 +120,76 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
@Override
public void onTrigger(CallbackHandler.Event event) {
if (event == updateCheckDone) {
if (event == Global.Events.updateCheckDone) {
Logger.dev("StatusFragment: Update Check UI refresh triggered");
updateCheckUI();
} else if (event == safetyNetDone) {
} else if (event == Global.Events.safetyNetDone) {
Logger.dev("StatusFragment: SafetyNet UI refresh triggered");
updateSafetyNetUI();
}
}
@Override
public void onResume() {
super.onResume();
CallbackHandler.register(updateCheckDone, this);
CallbackHandler.register(safetyNetDone, this);
if (updateCheckDone.isTriggered) {
public void onStart() {
super.onStart();
CallbackHandler.register(Global.Events.updateCheckDone, this);
CallbackHandler.register(Global.Events.safetyNetDone, this);
if (Global.Events.updateCheckDone.isTriggered) {
updateCheckUI();
}
if (safetyNetDone.isTriggered) {
if (Global.Events.safetyNetDone.isTriggered) {
updateSafetyNetUI();
}
getActivity().setTitle(R.string.status);
}
@Override
public void onPause() {
super.onPause();
CallbackHandler.unRegister(updateCheckDone, this);
CallbackHandler.unRegister(safetyNetDone, this);
public void onStop() {
CallbackHandler.unRegister(Global.Events.updateCheckDone, this);
CallbackHandler.unRegister(Global.Events.safetyNetDone, this);
super.onStop();
}
private static void checkMagiskInfo() {
List<String> ret = Shell.sh("getprop magisk.version");
if (ret.get(0).length() == 0) {
magiskVersion = -1;
} else {
try {
magiskVersionString = ret.get(0);
magiskVersion = Double.parseDouble(ret.get(0));
} catch (NumberFormatException e) {
// Custom version don't need to receive updates
magiskVersion = Double.POSITIVE_INFINITY;
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
private void updateUI() {
int image, color;
checkMagiskInfo();
Global.updateMagiskInfo();
if (magiskVersion < 0) {
if (Global.Info.magiskVersion < 0) {
magiskVersionText.setText(R.string.magisk_version_error);
} else if (Global.Info.disabled) {
magiskVersionText.setText(getString(R.string.magisk_version_disable, Global.Info.magiskVersionString));
} else {
magiskVersionText.setText(getString(R.string.magisk_version, magiskVersionString));
magiskVersionText.setText(getString(R.string.magisk_version, Global.Info.magiskVersionString));
}
if (Shell.rootStatus == 1) {
color = colorOK;
image = R.drawable.ic_check_circle;
rootStatusText.setText(R.string.proper_root);
rootInfoText.setText(Shell.sh("su -v").get(0));
} else {
rootInfoText.setText(R.string.root_info_warning);
if (Shell.rootStatus == 0) {
switch (Shell.rootStatus) {
case 0:
color = colorBad;
image = R.drawable.ic_cancel;
rootStatusText.setText(R.string.not_rooted);
} else {
rootInfoText.setText(R.string.root_info_warning);
break;
case 1:
if (Global.Info.suVersion != null) {
color = colorOK;
image = R.drawable.ic_check_circle;
rootStatusText.setText(R.string.proper_root);
rootInfoText.setText(Global.Info.suVersion);
break;
}
case -1:
default:
color = colorNeutral;
image = R.drawable.ic_help;
rootStatusText.setText(R.string.root_error);
}
rootInfoText.setText(R.string.root_info_warning);
}
rootStatusContainer.setBackgroundColor(color);
rootStatusText.setTextColor(color);
@@ -214,24 +200,28 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
private void updateCheckUI() {
int image, color;
if (remoteMagiskVersion < 0) {
if (Global.Info.remoteMagiskVersion < 0) {
color = colorNeutral;
image = R.drawable.ic_help;
magiskUpdateText.setText(R.string.cannot_check_updates);
} else if (remoteMagiskVersion > magiskVersion) {
} else if (Global.Info.remoteMagiskVersion > Global.Info.magiskVersion) {
color = colorInfo;
image = R.drawable.ic_update;
magiskUpdateText.setText(getString(R.string.magisk_update_available, remoteMagiskVersion));
magiskUpdateText.setText(getString(R.string.magisk_update_available, Global.Info.remoteMagiskVersion));
} else {
color = colorOK;
image = R.drawable.ic_check_circle;
magiskUpdateText.setText(getString(R.string.up_to_date, getString(R.string.magisk)));
}
if (magiskVersion < 0) {
if (Global.Info.magiskVersion < 0) {
color = colorBad;
image = R.drawable.ic_cancel;
} else if (Global.Info.disabled) {
color = colorNeutral;
image = R.drawable.ic_cancel;
}
magiskStatusContainer.setBackgroundColor(color);
magiskVersionText.setTextColor(color);
magiskUpdateText.setTextColor(color);
@@ -242,23 +232,23 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
updateMagisk = Utils.getAlertDialogBuilder(getActivity())
.setTitle(R.string.magisk_update_title)
.setMessage(getString(R.string.magisk_update_message, remoteMagiskVersion))
.setMessage(getString(R.string.magisk_update_message, Global.Info.remoteMagiskVersion))
.setCancelable(true)
.setPositiveButton(R.string.goto_install, (dialogInterface, i) -> {
((MainActivity) getActivity()).navigationView.setCheckedItem(R.id.install);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
try {
transaction.replace(R.id.content_frame, new InstallFragment(), "install").commit();
} catch (IllegalStateException ignored) {}
})
.setNeutralButton(R.string.check_release_notes, (dialog, which) -> {
getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(releaseNoteLink)));
getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Global.Info.releaseNoteLink)));
})
.setNegativeButton(R.string.no_thanks, null)
.create();
if (magiskVersion < remoteMagiskVersion && Shell.rootAccess()) {
if (Global.Info.magiskVersion < Global.Info.remoteMagiskVersion && Shell.rootAccess()) {
magiskStatusContainer.setOnClickListener(view -> updateMagisk.show());
if (!noDialog) {
noDialog = true;
@@ -270,7 +260,7 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
private void updateSafetyNetUI() {
int image, color;
safetyNetProgress.setVisibility(View.GONE);
switch (SNCheckResult) {
switch (Global.Info.SNCheckResult) {
case -3:
color = colorNeutral;
image = R.drawable.ic_help;

View File

@@ -0,0 +1,92 @@
package com.topjohnwu.magisk;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.adapters.SuLogAdapter;
import com.topjohnwu.magisk.superuser.SuLogDatabaseHelper;
import com.topjohnwu.magisk.superuser.SuLogEntry;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class SuLogFragment extends Fragment {
@BindView(R.id.empty_rv) TextView emptyRv;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
private Unbinder unbinder;
private SuLogDatabaseHelper dbHelper;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.menu_log, menu);
menu.findItem(R.id.menu_save).setVisible(false);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_su_log, container, false);
unbinder = ButterKnife.bind(this, v);
dbHelper = new SuLogDatabaseHelper(getActivity());
updateList();
return v;
}
private void updateList() {
List<SuLogEntry> logs = dbHelper.getLogList();
if (logs.size() == 0) {
emptyRv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
recyclerView.setAdapter(new SuLogAdapter(logs).getAdapter());
emptyRv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_refresh:
updateList();
return true;
case R.id.menu_clear:
dbHelper.clearLogs();
updateList();
return true;
default:
return true;
}
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@@ -0,0 +1,64 @@
package com.topjohnwu.magisk;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.topjohnwu.magisk.adapters.PolicyAdapter;
import com.topjohnwu.magisk.superuser.Policy;
import com.topjohnwu.magisk.superuser.SuDatabaseHelper;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.Unbinder;
public class SuperuserFragment extends Fragment {
private Unbinder unbinder;
@BindView(R.id.recyclerView) RecyclerView recyclerView;
@BindView(R.id.empty_rv) TextView emptyRv;
@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);
PackageManager pm = getActivity().getPackageManager();
SuDatabaseHelper dbHelper = new SuDatabaseHelper(getActivity());
List<Policy> policyList = dbHelper.getPolicyList(pm);
if (policyList.size() == 0) {
emptyRv.setVisibility(View.VISIBLE);
recyclerView.setVisibility(View.GONE);
} else {
recyclerView.setAdapter(new PolicyAdapter(policyList, dbHelper, pm));
emptyRv.setVisibility(View.GONE);
recyclerView.setVisibility(View.VISIBLE);
}
return view;
}
@Override
public void onStart() {
super.onStart();
getActivity().setTitle(getString(R.string.superuser));
}
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}

View File

@@ -2,6 +2,7 @@ package com.topjohnwu.magisk.adapters;
import android.content.pm.ApplicationInfo;
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;
@@ -16,6 +17,7 @@ import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Utils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -24,6 +26,17 @@ import butterknife.ButterKnife;
public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.ViewHolder> {
public static final List<String> BLACKLIST = Arrays.asList(
"android",
"com.topjohnwu.magisk",
"com.google.android.gms"
);
private static final List<String> SNLIST = Arrays.asList(
"com.google.android.apps.walletnfcrel",
"com.nianticlabs.pokemongo"
);
private List<ApplicationInfo> mOriginalList, mList;
private List<String> mHideList;
private PackageManager packageManager;
@@ -33,6 +46,7 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
mOriginalList = mList = Collections.emptyList();
mHideList = Collections.emptyList();
this.packageManager = packageManager;
filter = new ApplicationFilter();
}
public void setLists(List<ApplicationInfo> listApps, List<String> hideList) {
@@ -44,7 +58,6 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View mView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_app, parent, false);
ButterKnife.bind(this, mView);
return new ViewHolder(mView);
}
@@ -56,17 +69,31 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
holder.appName.setText(info.loadLabel(packageManager));
holder.appPackage.setText(info.packageName);
// Remove all listeners
holder.itemView.setOnClickListener(null);
holder.checkBox.setOnCheckedChangeListener(null);
holder.checkBox.setChecked(mHideList.contains(info.packageName));
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
if (isChecked) {
new Async.MagiskHide().add(info.packageName);
mHideList.add(info.packageName);
} else {
new Async.MagiskHide().rm(info.packageName);
mHideList.remove(info.packageName);
}
});
if (SNLIST.contains(info.packageName)) {
holder.checkBox.setChecked(true);
holder.checkBox.setEnabled(false);
holder.itemView.setOnClickListener(v -> {
Snackbar snackbar = Snackbar.make(holder.itemView, R.string.safetyNet_hide_notice, Snackbar.LENGTH_LONG);
((TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text)).setMaxLines(2);
snackbar.show();
});
} else {
holder.checkBox.setEnabled(true);
holder.checkBox.setChecked(mHideList.contains(info.packageName));
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
if (isChecked) {
new Async.MagiskHide().add(info.packageName);
mHideList.add(info.packageName);
} else {
new Async.MagiskHide().rm(info.packageName);
mHideList.remove(info.packageName);
}
});
}
}
@Override
@@ -75,9 +102,6 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
}
public void filter(String constraint) {
if (filter == null) {
filter = new ApplicationFilter();
}
filter.filter(constraint);
}

View File

@@ -32,7 +32,6 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_module, parent, false);
ButterKnife.bind(this, view);
return new ViewHolder(view);
}
@@ -41,11 +40,15 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
Context context = holder.itemView.getContext();
final Module module = mList.get(position);
holder.title.setText(module.getName());
holder.versionName.setText(module.getVersion());
String version = module.getVersion();
String author = module.getAuthor();
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
holder.description.setText(module.getDescription());
String description = module.getDescription();
String noInfo = context.getString(R.string.no_info_provided);
holder.title.setText(module.getName());
holder.versionName.setText( TextUtils.isEmpty(version) ? noInfo : version);
holder.author.setText( TextUtils.isEmpty(author) ? noInfo : context.getString(R.string.author, author));
holder.description.setText( TextUtils.isEmpty(description) ? noInfo : description);
holder.checkBox.setOnCheckedChangeListener(null);
holder.checkBox.setChecked(module.isEnabled());
@@ -62,8 +65,8 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
@Override
protected void onPostExecute(Void v) {
int title = isChecked ? R.string.disable_file_removed : R.string.disable_file_created;
Snackbar.make(holder.title, title, Snackbar.LENGTH_SHORT).show();
int snack = isChecked ? R.string.disable_file_removed : R.string.disable_file_created;
Snackbar.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
}
}.exec());
@@ -82,8 +85,8 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
@Override
protected void onPostExecute(Void v) {
int title = removed ? R.string.remove_file_deleted : R.string.remove_file_created;
Snackbar.make(holder.title, title, Snackbar.LENGTH_SHORT).show();
int snack = removed ? R.string.remove_file_deleted : R.string.remove_file_created;
Snackbar.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
updateDeleteButton(holder, module);
}
}.exec());

View File

@@ -0,0 +1,222 @@
package com.topjohnwu.magisk.adapters;
import android.animation.Animator;
import android.animation.ValueAnimator;
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;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Switch;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.superuser.Policy;
import com.topjohnwu.magisk.superuser.SuDatabaseHelper;
import com.topjohnwu.magisk.utils.Utils;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import butterknife.BindView;
import butterknife.ButterKnife;
public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder> {
private List<Policy> policyList;
private SuDatabaseHelper dbHelper;
private PackageManager pm;
private Set<Policy> expandList = new HashSet<>();
public PolicyAdapter(List<Policy> list, SuDatabaseHelper db, PackageManager pm) {
policyList = list;
dbHelper = db;
this.pm = pm;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_policy, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Policy policy = policyList.get(position);
try {
holder.setExpanded(expandList.contains(policy));
holder.itemView.setOnClickListener(view -> {
if (holder.mExpanded) {
holder.collapse();
expandList.remove(policy);
} else {
holder.expand();
expandList.add(policy);
}
});
holder.appName.setText(policy.appName);
holder.packageName.setText(policy.packageName);
holder.appIcon.setImageDrawable(pm.getPackageInfo(policy.packageName, 0).applicationInfo.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);
Snackbar.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.addPolicy(policy);
}
});
holder.notificationSwitch.setOnCheckedChangeListener((v, isChecked) -> {
if ((isChecked && !policy.notification) ||
(!isChecked && policy.notification)) {
policy.notification = isChecked;
String message = v.getContext().getString(
isChecked ? R.string.su_snack_notif_on : R.string.su_snack_notif_off, policy.appName);
Snackbar.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.addPolicy(policy);
}
});
holder.loggingSwitch.setOnCheckedChangeListener((v, isChecked) -> {
if ((isChecked && !policy.logging) ||
(!isChecked && policy.logging)) {
policy.logging = isChecked;
String message = v.getContext().getString(
isChecked ? R.string.su_snack_log_on : R.string.su_snack_log_off, policy.appName);
Snackbar.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.addPolicy(policy);
}
});
holder.delete.setOnClickListener(v -> Utils.getAlertDialogBuilder(v.getContext())
.setTitle(R.string.su_revoke_title)
.setMessage(v.getContext().getString(R.string.su_revoke_msg, policy.appName))
.setPositiveButton(R.string.yes, (dialog, which) -> {
policyList.remove(position);
notifyItemRemoved(position);
notifyItemRangeChanged(position, policyList.size());
Snackbar.make(holder.itemView, v.getContext().getString(R.string.su_snack_revoke, policy.appName),
Snackbar.LENGTH_SHORT).show();
dbHelper.deletePolicy(policy.uid);
})
.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);
} catch (PackageManager.NameNotFoundException e) {
policyList.remove(position);
dbHelper.deletePolicy(policy.uid);
onBindViewHolder(holder, position);
}
}
@Override
public int getItemCount() {
return policyList.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.app_name) TextView appName;
@BindView(R.id.package_name) TextView packageName;
@BindView(R.id.expand_layout) LinearLayout expandLayout;
@BindView(R.id.app_icon) ImageView appIcon;
@BindView(R.id.master_switch) Switch masterSwitch;
@BindView(R.id.notification_switch) Switch notificationSwitch;
@BindView(R.id.logging_switch) Switch loggingSwitch;
@BindView(R.id.delete) ImageView delete;
@BindView(R.id.more_info) ImageView moreInfo;
private ValueAnimator mAnimator;
private boolean mExpanded = false;
private static int expandHeight = 0;
ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
expandLayout.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (expandHeight == 0) {
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
expandLayout.measure(widthSpec, heightSpec);
expandHeight = expandLayout.getMeasuredHeight();
}
expandLayout.getViewTreeObserver().removeOnPreDrawListener(this);
expandLayout.setVisibility(View.GONE);
mAnimator = slideAnimator(0, expandHeight);
return true;
}
});
}
private void setExpanded(boolean expanded) {
mExpanded = expanded;
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
layoutParams.height = expanded ? expandHeight : 0;
expandLayout.setLayoutParams(layoutParams);
expandLayout.setVisibility(expanded ? View.VISIBLE : View.GONE);
}
private void expand() {
expandLayout.setVisibility(View.VISIBLE);
mAnimator.start();
mExpanded = true;
}
private void collapse() {
if (!mExpanded) return;
int finalHeight = expandLayout.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
expandLayout.setVisibility(View.GONE);
}
@Override
public void onAnimationStart(Animator animator) {}
@Override
public void onAnimationCancel(Animator animator) {}
@Override
public void onAnimationRepeat(Animator animator) {}
});
mAnimator.start();
mExpanded = false;
}
private ValueAnimator slideAnimator(int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(valueAnimator -> {
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
layoutParams.height = value;
expandLayout.setLayoutParams(layoutParams);
});
return animator;
}
}
}

View File

@@ -24,6 +24,7 @@ import com.topjohnwu.magisk.utils.WebWindow;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -31,7 +32,7 @@ import butterknife.ButterKnife;
public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder> {
private List<Repo> mUpdateRepos, mInstalledRepos, mOthersRepos;
private HashSet<Repo> expandList = new HashSet<>();
private Set<Repo> expandList = new HashSet<>();
public ReposAdapter(List<Repo> update, List<Repo> installed, List<Repo> others) {
mUpdateRepos = update;
@@ -42,7 +43,6 @@ public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder>
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_repo, parent, false);
ButterKnife.bind(this, v);
return new ViewHolder(v);
}

View File

@@ -1,6 +1,5 @@
package com.topjohnwu.magisk.adapters;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.LayoutInflater;
@@ -13,25 +12,21 @@ import java.util.Comparator;
public class SimpleSectionedRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Context mContext;
private static final int SECTION_TYPE = 0;
private boolean mValid = true;
private int mSectionResourceId;
private int mTextResourceId;
private LayoutInflater mLayoutInflater;
private RecyclerView.Adapter mBaseAdapter;
private SparseArray<Section> mSections = new SparseArray<Section>();
public SimpleSectionedRecyclerViewAdapter(Context context, int sectionResourceId, int textResourceId,
public SimpleSectionedRecyclerViewAdapter(int sectionResourceId, int textResourceId,
RecyclerView.Adapter baseAdapter) {
mLayoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mSectionResourceId = sectionResourceId;
mTextResourceId = textResourceId;
mBaseAdapter = baseAdapter;
mContext = context;
mBaseAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
@@ -74,7 +69,7 @@ public class SimpleSectionedRecyclerViewAdapter extends RecyclerView.Adapter<Rec
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int typeView) {
if (typeView == SECTION_TYPE) {
final View view = LayoutInflater.from(mContext).inflate(mSectionResourceId, parent, false);
View view = LayoutInflater.from(parent.getContext()).inflate(mSectionResourceId, parent, false);
return new SectionViewHolder(view,mTextResourceId);
}else{
return mBaseAdapter.onCreateViewHolder(parent, typeView -1);

View File

@@ -0,0 +1,240 @@
package com.topjohnwu.magisk.adapters;
import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.animation.Animation;
import android.view.animation.RotateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.thoughtbot.expandablerecyclerview.ExpandableRecyclerViewAdapter;
import com.thoughtbot.expandablerecyclerview.models.ExpandableGroup;
import com.thoughtbot.expandablerecyclerview.viewholders.ChildViewHolder;
import com.thoughtbot.expandablerecyclerview.viewholders.GroupViewHolder;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.superuser.SuLogEntry;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import butterknife.BindView;
import butterknife.ButterKnife;
public class SuLogAdapter {
private ExpandableAdapter adapter;
private Set<SuLogEntry> expandList = new HashSet<>();
public SuLogAdapter(List<SuLogEntry> list) {
// Separate the logs with date
Map<String, List<SuLogEntry>> logEntryMap = new LinkedHashMap<>();
List<SuLogEntry> group;
for (SuLogEntry log : list) {
String date = log.getDateString();
group = logEntryMap.get(date);
if (group == null) {
group = new ArrayList<>();
logEntryMap.put(date, group);
}
group.add(log);
}
// Then format them into expandable groups
List<LogGroup> logEntryGroups = new ArrayList<>();
for (Map.Entry<String, List<SuLogEntry>> entry : logEntryMap.entrySet()) {
logEntryGroups.add(new LogGroup(entry.getKey(), entry.getValue()));
}
adapter = new ExpandableAdapter(logEntryGroups);
}
public RecyclerView.Adapter getAdapter() {
return adapter;
}
private class ExpandableAdapter
extends ExpandableRecyclerViewAdapter<LogGroupViewHolder, LogViewHolder> {
ExpandableAdapter(List<? extends ExpandableGroup> groups) {
super(groups);
expandableList.expandedGroupIndexes[0] = true;
}
@Override
public LogGroupViewHolder onCreateGroupViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog_group, parent, false);
return new LogGroupViewHolder(v);
}
@Override
public LogViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_sulog, parent, false);
return new LogViewHolder(v);
}
@Override
public void onBindChildViewHolder(LogViewHolder holder, int flatPosition, ExpandableGroup group, int childIndex) {
Context context = holder.itemView.getContext();
SuLogEntry logEntry = (SuLogEntry) group.getItems().get(childIndex);
holder.setExpanded(expandList.contains(logEntry));
holder.itemView.setOnClickListener(view -> {
if (holder.mExpanded) {
holder.collapse();
expandList.remove(logEntry);
} else {
holder.expand();
expandList.add(logEntry);
}
});
holder.appName.setText(logEntry.appName);
holder.action.setText(context.getString(logEntry.action ? R.string.grant : R.string.deny));
holder.command.setText(logEntry.command);
holder.fromPid.setText(String.valueOf(logEntry.fromPid));
holder.toUid.setText(String.valueOf(logEntry.toUid));
holder.time.setText(logEntry.getTimeString());
}
@Override
public void onBindGroupViewHolder(LogGroupViewHolder holder, int flatPosition, ExpandableGroup group) {
holder.date.setText(group.getTitle());
if (isGroupExpanded(flatPosition)) holder.expand();
}
}
private class LogGroup extends ExpandableGroup<SuLogEntry> {
LogGroup(String title, List<SuLogEntry> items) {
super(title, items);
}
}
static class LogGroupViewHolder extends GroupViewHolder {
@BindView(R.id.date) TextView date;
@BindView(R.id.arrow) ImageView arrow;
public LogGroupViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
@Override
public void expand() {
RotateAnimation rotate =
new RotateAnimation(360, 180, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(300);
rotate.setFillAfter(true);
arrow.setAnimation(rotate);
}
@Override
public void collapse() {
RotateAnimation rotate =
new RotateAnimation(180, 360, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);
rotate.setDuration(300);
rotate.setFillAfter(true);
arrow.setAnimation(rotate);
}
}
static class LogViewHolder extends ChildViewHolder {
@BindView(R.id.app_name) TextView appName;
@BindView(R.id.action) TextView action;
@BindView(R.id.time) TextView time;
@BindView(R.id.fromPid) TextView fromPid;
@BindView(R.id.toUid) TextView toUid;
@BindView(R.id.command) TextView command;
@BindView(R.id.expand_layout) LinearLayout expandLayout;
private ValueAnimator mAnimator;
private boolean mExpanded = false;
private static int expandHeight = 0;
public LogViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
expandLayout.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
if (expandHeight == 0) {
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
expandLayout.measure(widthSpec, heightSpec);
expandHeight = expandLayout.getMeasuredHeight();
}
expandLayout.getViewTreeObserver().removeOnPreDrawListener(this);
expandLayout.setVisibility(View.GONE);
mAnimator = slideAnimator(0, expandHeight);
return true;
}
});
}
private void setExpanded(boolean expanded) {
mExpanded = expanded;
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
layoutParams.height = expanded ? expandHeight : 0;
expandLayout.setLayoutParams(layoutParams);
expandLayout.setVisibility(expanded ? View.VISIBLE : View.GONE);
}
private void expand() {
expandLayout.setVisibility(View.VISIBLE);
mAnimator.start();
mExpanded = true;
}
private void collapse() {
if (!mExpanded) return;
int finalHeight = expandLayout.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
@Override
public void onAnimationEnd(Animator animator) {
expandLayout.setVisibility(View.GONE);
}
@Override
public void onAnimationStart(Animator animator) {}
@Override
public void onAnimationCancel(Animator animator) {}
@Override
public void onAnimationRepeat(Animator animator) {}
});
mAnimator.start();
mExpanded = false;
}
private ValueAnimator slideAnimator(int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(valueAnimator -> {
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
layoutParams.height = value;
expandLayout.setLayoutParams(layoutParams);
});
return animator;
}
}
}

View File

@@ -0,0 +1,41 @@
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;
public class TabFragmentAdapter extends FragmentPagerAdapter {
private List<Fragment> fragmentList;
private List<String> titleList;
public TabFragmentAdapter(FragmentManager fm) {
super(fm);
fragmentList = new ArrayList<>();
titleList = new ArrayList<>();
}
@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
@Override
public int getCount() {
return fragmentList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return titleList.get(position);
}
public void addTab(Fragment fragment, String title) {
fragmentList.add(fragment);
titleList.add(title);
}
}

View File

@@ -24,7 +24,7 @@ public class Module extends BaseModule {
if (mName == null)
mName = mId;
Logger.dev("Creating Module, id: " + mId);
Logger.dev("Creating Data, id: " + mId);
mEnable = !Utils.itemExist(mDisableFile);
mRemove = Utils.itemExist(mRemoveFile);

View File

@@ -1,15 +1,18 @@
package com.topjohnwu.magisk.utils;
package com.topjohnwu.magisk.module;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
import android.widget.Toast;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.topjohnwu.magisk.Global;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.module.BaseModule;
import com.topjohnwu.magisk.module.Module;
import com.topjohnwu.magisk.module.Repo;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ValueSortedMap;
import com.topjohnwu.magisk.utils.WebService;
import org.json.JSONArray;
import org.json.JSONException;
@@ -17,9 +20,6 @@ import org.json.JSONObject;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
@@ -27,32 +27,30 @@ import java.util.Locale;
import java.util.Map;
public class ModuleHelper {
private static final String MAGISK_PATH = "/magisk";
public static final String FILE_KEY = "RepoMap";
private static final int GSON_DB_VER = 1;
private static final String ETAG_KEY = "ETag";
private static final String VERSION_KEY = "version";
private static final String REPO_KEY = "repomap";
public static final String VERSION_KEY = "version";
public static final String ETAG_KEY = "ETag";
private static final int DATABASE_VER = 1;
private static ValueSortedMap<String, Repo> repoMap = new ValueSortedMap<>();
private static ValueSortedMap<String, Module> moduleMap = new ValueSortedMap<>();
private static final String FILE_KEY = "RepoMap";
public static void createModuleMap() {
Logger.dev("ModuleHelper: Loading modules");
moduleMap.clear();
Global.Data.moduleMap = new ValueSortedMap<>();
for (String path : Utils.getModList(MAGISK_PATH)) {
Logger.dev("ModuleHelper: Adding modules from " + path);
Module module;
try {
module = new Module(path);
moduleMap.put(module.getId(), module);
Global.Data.moduleMap.put(module.getId(), module);
} catch (BaseModule.CacheModException ignored) {}
}
Logger.dev("ModuleHelper: Module load done");
Logger.dev("ModuleHelper: Data load done");
}
public static void createRepoMap(Context context) {
@@ -60,20 +58,20 @@ public class ModuleHelper {
SharedPreferences prefs = context.getSharedPreferences(FILE_KEY, Context.MODE_PRIVATE);
repoMap.clear();
Global.Data.repoMap = new ValueSortedMap<>();
Gson gson = new Gson();
String jsonString;
int cachedVersion = prefs.getInt(VERSION_KEY, 0);
if (cachedVersion != DATABASE_VER) {
if (cachedVersion != GSON_DB_VER) {
// Ignore incompatible cached database
jsonString = null;
} else {
jsonString = prefs.getString(REPO_KEY, null);
}
ValueSortedMap<String, Repo> cached = null;
Map<String, Repo> cached = null;
if (jsonString != null) {
cached = gson.fromJson(jsonString, new TypeToken<ValueSortedMap<String, Repo>>(){}.getType());
@@ -85,7 +83,7 @@ public class ModuleHelper {
// Get cached ETag to add in the request header
String etag = prefs.getString(ETAG_KEY, "");
HashMap<String, String> header = new HashMap<>();
Map<String, String> header = new HashMap<>();
header.put("If-None-Match", etag);
// Making a request to main URL for repo info
@@ -123,7 +121,7 @@ public class ModuleHelper {
repo.update(updatedDate);
}
if (repo.getId() != null) {
repoMap.put(id, repo);
Global.Data.repoMap.put(id, repo);
}
} catch (BaseModule.CacheModException ignored) {}
}
@@ -133,12 +131,12 @@ public class ModuleHelper {
} else {
// Use cached if no internet or no updates
Logger.dev("ModuleHelper: No updates, use cached");
repoMap.putAll(cached);
Global.Data.repoMap.putAll(cached);
}
prefs.edit()
.putInt(VERSION_KEY, DATABASE_VER)
.putString(REPO_KEY, gson.toJson(repoMap))
.putInt(VERSION_KEY, GSON_DB_VER)
.putString(REPO_KEY, gson.toJson(Global.Data.repoMap))
.putString(ETAG_KEY, etag)
.apply();
@@ -147,15 +145,15 @@ public class ModuleHelper {
public static void getModuleList(List<Module> moduleList) {
moduleList.clear();
moduleList.addAll(moduleMap.values());
moduleList.addAll(Global.Data.moduleMap.values());
}
public static void getRepoLists(List<Repo> update, List<Repo> installed, List<Repo> others) {
update.clear();
installed.clear();
others.clear();
for (Repo repo : repoMap.values()) {
Module module = moduleMap.get(repo.getId());
for (Repo repo : Global.Data.repoMap.values()) {
Module module = Global.Data.moduleMap.get(repo.getId());
if (module != null) {
if (repo.getVersionCode() > module.getVersionCode()) {
update.add(repo);
@@ -168,36 +166,15 @@ public class ModuleHelper {
}
}
private static class ValueSortedMap<K, V extends Comparable > extends HashMap<K, V> {
private List<V> sorted = new ArrayList<>();
@NonNull
@Override
public Collection<V> values() {
if (sorted.isEmpty()) {
sorted.addAll(super.values());
Collections.sort(sorted);
}
return sorted;
}
@Override
public V put(K key, V value) {
sorted.clear();
return super.put(key, value);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
sorted.clear();
super.putAll(m);
}
@Override
public V remove(Object key) {
sorted.clear();
return super.remove(key);
}
public static void clearRepoCache(Context context) {
SharedPreferences repoMap = context.getSharedPreferences(FILE_KEY, Context.MODE_PRIVATE);
repoMap.edit()
.remove(ETAG_KEY)
.remove(VERSION_KEY)
.apply();
Global.Events.repoLoadDone.isTriggered = false;
new Async.LoadRepos(context).exec();
Toast.makeText(context, R.string.repo_cache_cleared, Toast.LENGTH_SHORT).show();
}
}

View File

@@ -0,0 +1,26 @@
package com.topjohnwu.magisk.receivers;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.widget.Toast;
import com.topjohnwu.magisk.Global;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Async;
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Global.initSuAccess();
Global.updateMagiskInfo();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (prefs.getBoolean("magiskhide", false) && !Global.Info.disabled && Global.Info.magiskVersion > 10.3) {
Toast.makeText(context, R.string.start_magiskhide, Toast.LENGTH_SHORT).show();
new Async.MagiskHide(true).enable();
}
}
}

View File

@@ -2,8 +2,8 @@ package com.topjohnwu.magisk.receivers;
import android.net.Uri;
import com.topjohnwu.magisk.Global;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.StatusFragment;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.ZipUtils;
@@ -56,7 +56,7 @@ public class MagiskDlReceiver extends DownloadReceiver {
@Override
protected Void doInBackground(Void... params) {
Shell.su("setprop magisk.version "
+ String.valueOf(StatusFragment.remoteMagiskVersion));
+ String.valueOf(Global.Info.remoteMagiskVersion));
return null;
}
}.exec();

View File

@@ -4,7 +4,7 @@ import android.net.Uri;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Utils.ByteArrayInOutStream;
import com.topjohnwu.magisk.utils.ByteArrayInOutStream;
import com.topjohnwu.magisk.utils.ZipUtils;
import java.io.OutputStream;
@@ -33,9 +33,9 @@ public class RepoDlReceiver extends DownloadReceiver {
ZipUtils.signZip(mContext, buffer.getInputStream(), buffer, true);
// Write it back to the downloaded zip
OutputStream out = mContext.getContentResolver().openOutputStream(mUri);
buffer.writeTo(out);
out.close();
try (OutputStream out = mContext.getContentResolver().openOutputStream(mUri)) {
buffer.writeTo(out);
}
}
}.exec();
}

View File

@@ -0,0 +1,51 @@
package com.topjohnwu.magisk.superuser;
import android.content.ContentValues;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.database.Cursor;
public class Policy {
public static final int INTERACTIVE = 0;
public static final int DENY = 1;
public static final int ALLOW = 2;
public int uid, policy;
public long until;
public boolean logging = true, notification = true;
public String packageName, appName;
public PackageInfo info;
public Policy(int uid, PackageManager pm) throws PackageManager.NameNotFoundException {
String[] pkgs = pm.getPackagesForUid(uid);
if (pkgs != null && pkgs.length > 0) {
info = pm.getPackageInfo(pkgs[0], 0);
this.uid = uid;
packageName = pkgs[0];
appName = info.applicationInfo.loadLabel(pm).toString();
} else throw new PackageManager.NameNotFoundException();
}
public Policy(Cursor c) {
uid = c.getInt(c.getColumnIndex("uid"));
packageName = c.getString(c.getColumnIndex("package_name"));
appName = c.getString(c.getColumnIndex("app_name"));
policy = c.getInt(c.getColumnIndex("policy"));
until = c.getLong(c.getColumnIndex("until"));
logging = c.getInt(c.getColumnIndex("logging")) != 0;
notification = c.getInt(c.getColumnIndex("notification")) != 0;
}
public ContentValues getContentValues() {
ContentValues values = new ContentValues();
values.put("uid", uid);
values.put("package_name", packageName);
values.put("app_name", appName);
values.put("policy", policy);
values.put("until", until);
values.put("logging", logging ? 1 : 0);
values.put("notification", notification ? 1 : 0);
return values;
}
}

View File

@@ -0,0 +1,27 @@
package com.topjohnwu.magisk.superuser;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.topjohnwu.magisk.Global;
public class RequestActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent intent = getIntent();
if (intent == null) {
finish();
return;
}
Global.initSuConfigs(this);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setClass(this, SuRequestActivity.class);
startActivity(intent);
finish();
}
}

View File

@@ -0,0 +1,79 @@
package com.topjohnwu.magisk.superuser;
import android.content.Context;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import java.util.ArrayList;
import java.util.List;
public class SuDatabaseHelper extends SQLiteOpenHelper {
private static final int DATABASE_VER = 1;
private static final String TABLE_NAME = "policies";
public SuDatabaseHelper(Context context) {
super(context, "su.db", null, DATABASE_VER);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
"(uid INT, package_name TEXT, app_name TEXT, policy INT, " +
"until INT, logging INT, notification INT, " +
"PRIMARY KEY(uid))");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Currently new database, no upgrading
}
public boolean deletePolicy(int uid) {
SQLiteDatabase db = getWritableDatabase();
db.delete(TABLE_NAME, "uid=?", new String[] { String.valueOf(uid) });
db.close();
return getPolicy(uid) == null;
}
public Policy getPolicy(int uid) {
Policy policy = null;
SQLiteDatabase db = getReadableDatabase();
try (Cursor c = db.query(TABLE_NAME, null, "uid=?", new String[] { String.valueOf(uid) }, null, null, null)) {
if (c.moveToNext())
policy = new Policy(c);
}
db.close();
return policy;
}
public void addPolicy(Policy policy) {
SQLiteDatabase db = getWritableDatabase();
db.replace(TABLE_NAME, null, policy.getContentValues());
db.close();
}
public List<Policy> getPolicyList(PackageManager pm) {
List<Policy> ret = new ArrayList<>();
SQLiteDatabase db = getWritableDatabase();
Policy policy;
// Clear outdated policies
db.delete(TABLE_NAME, "until > 0 and until < ?", new String[] { String.valueOf(System.currentTimeMillis()) });
try (Cursor c = db.query(TABLE_NAME, null, null, null, null, null, "app_name ASC")) {
while (c.moveToNext()) {
policy = new Policy(c);
// Package is uninstalled
if (pm.getPackagesForUid(policy.uid) == null) {
deletePolicy(policy.uid);
} else {
ret.add(policy);
}
}
}
db.close();
return ret;
}
}

View File

@@ -0,0 +1,68 @@
package com.topjohnwu.magisk.superuser;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import com.topjohnwu.magisk.Global;
import java.util.ArrayList;
import java.util.List;
public class SuLogDatabaseHelper extends SQLiteOpenHelper {
private static final int DATABASE_VER = 1;
private static final String TABLE_NAME = "logs";
public SuLogDatabaseHelper(Context context) {
super(context, "sulog.db", null, DATABASE_VER);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, " +
"to_uid INT, action INT, time INT, command TEXT)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Currently new database, no upgrading
}
public void addLog(SuLogEntry log) {
SQLiteDatabase db = getWritableDatabase();
db.insert(TABLE_NAME, null, log.getContentValues());
db.close();
}
public void clearLogs() {
SQLiteDatabase db = getWritableDatabase();
db.delete(TABLE_NAME, null, null);
db.close();
}
public List<SuLogEntry> getLogList() {
return getLogList(null);
}
public List<SuLogEntry> getLogList(int uid) {
return getLogList("uid=" + uid);
}
public List<SuLogEntry> getLogList(String selection) {
List<SuLogEntry> ret = new ArrayList<>();
SQLiteDatabase db = getWritableDatabase();
// Clear outdated logs
db.delete(TABLE_NAME, "time < ?", new String[] { String.valueOf(
System.currentTimeMillis() / 1000 - Global.Configs.suLogTimeout * 86400) });
try (Cursor c = db.query(TABLE_NAME, null, selection, null, null, null, "time DESC")) {
while (c.moveToNext())
ret.add(new SuLogEntry(c));
}
db.close();
return ret;
}
}

View File

@@ -0,0 +1,98 @@
package com.topjohnwu.magisk.superuser;
import android.content.ContentValues;
import android.database.Cursor;
import android.os.Parcel;
import android.os.Parcelable;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class SuLogEntry implements Parcelable {
public int fromUid, toUid, fromPid;
public String packageName, appName, command;
public boolean action;
public Date date;
public SuLogEntry(Policy policy) {
fromUid = policy.uid;
packageName = policy.packageName;
appName = policy.appName;
}
public SuLogEntry(Cursor c) {
fromUid = c.getInt(c.getColumnIndex("from_uid"));
fromPid = c.getInt(c.getColumnIndex("from_pid"));
toUid = c.getInt(c.getColumnIndex("to_uid"));
packageName = c.getString(c.getColumnIndex("package_name"));
appName = c.getString(c.getColumnIndex("app_name"));
command = c.getString(c.getColumnIndex("command"));
action = c.getInt(c.getColumnIndex("action")) != 0;
date = new Date(c.getLong(c.getColumnIndex("time")) * 1000);
}
public ContentValues getContentValues() {
ContentValues values = new ContentValues();
values.put("from_uid", fromUid);
values.put("package_name", packageName);
values.put("app_name", appName);
values.put("from_pid", fromPid);
values.put("command", command);
values.put("to_uid", toUid);
values.put("action", action ? 1 : 0);
values.put("time", date.getTime() / 1000);
return values;
}
public String getDateString() {
return DateFormat.getDateInstance(DateFormat.MEDIUM).format(date);
}
public String getTimeString() {
return new SimpleDateFormat("h:mm a", Locale.US).format(date);
}
public static final Creator<SuLogEntry> CREATOR = new Creator<SuLogEntry>() {
@Override
public SuLogEntry createFromParcel(Parcel in) {
return new SuLogEntry(in);
}
@Override
public SuLogEntry[] newArray(int size) {
return new SuLogEntry[size];
}
};
protected SuLogEntry(Parcel in) {
fromUid = in.readInt();
toUid = in.readInt();
fromPid = in.readInt();
packageName = in.readString();
appName = in.readString();
command = in.readString();
action = in.readByte() != 0;
date = new Date(in.readLong());
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(fromUid);
dest.writeInt(toUid);
dest.writeInt(fromPid);
dest.writeString(packageName);
dest.writeString(appName);
dest.writeString(command);
dest.writeByte((byte) (action ? 1 : 0));
dest.writeLong(date.getTime());
}
}

View File

@@ -0,0 +1,79 @@
package com.topjohnwu.magisk.superuser;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Process;
import android.widget.Toast;
import com.topjohnwu.magisk.Global;
import com.topjohnwu.magisk.R;
import java.util.Date;
public class SuReceiver extends BroadcastReceiver {
private static final int NO_NOTIFICATION = 0;
private static final int TOAST = 1;
@Override
public void onReceive(Context context, Intent intent) {
int fromUid, toUid, pid;
String command, action;
Policy policy;
if (intent == null) 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;
SuDatabaseHelper suDbHelper = new SuDatabaseHelper(context);
policy = suDbHelper.getPolicy(fromUid);
if (policy == null) try {
policy = new Policy(fromUid, context.getPackageManager());
} catch (Throwable throwable) {
return;
}
Global.initSuConfigs(context);
SuLogEntry log = new SuLogEntry(policy);
String message;
switch (action) {
case "allow":
message = context.getString(R.string.su_allow_toast, policy.appName);
log.action = true;
break;
case "deny":
message = context.getString(R.string.su_deny_toast, policy.appName);
log.action = false;
break;
default:
return;
}
if (policy.notification && Global.Configs.suNotificationType == TOAST)
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
if (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();
SuLogDatabaseHelper logDbHelper = new SuLogDatabaseHelper(context);
logDbHelper.addLog(log);
}
}
}

View File

@@ -0,0 +1,253 @@
package com.topjohnwu.magisk.superuser;
import android.content.ContentValues;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.FileObserver;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
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.Global;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.CallbackHandler;
import java.io.DataInputStream;
import java.io.IOException;
import butterknife.BindView;
import butterknife.ButterKnife;
public class SuRequestActivity extends AppCompatActivity implements CallbackHandler.EventListener {
private static final int[] timeoutList = {0, -1, 10, 20, 30, 60};
private static final int SU_PROTOCOL_PARAM_MAX = 20;
private static final int SU_PROTOCOL_NAME_MAX = 20;
private static final int SU_PROTOCOL_VALUE_MAX = 256;
private static final int PROMPT = 0;
private static final int AUTO_DENY = 1;
private static final int AUTO_ALLOW = 2;
@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;
private String socketPath;
private LocalSocket socket;
private PackageManager pm;
private int uid;
private Policy policy;
private CountDownTimer timer;
private CallbackHandler.EventListener self;
private CallbackHandler.Event event = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
pm = getPackageManager();
Intent intent = getIntent();
socketPath = intent.getStringExtra("socket");
self = this;
new FileObserver(socketPath) {
@Override
public void onEvent(int fileEvent, String path) {
if (fileEvent == FileObserver.DELETE_SELF) {
if (event != null)
event.trigger();
finish();
}
}
}.startWatching();
new SocketManager().exec();
}
void showRequest() {
switch (Global.Configs.suResponseType) {
case AUTO_DENY:
handleAction(Policy.DENY, 0);
return;
case AUTO_ALLOW:
handleAction(Policy.ALLOW, 0);
return;
case PROMPT:
default:
}
setContentView(R.layout.activity_request);
ButterKnife.bind(this);
appIcon.setImageDrawable(policy.info.applicationInfo.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(Global.Configs.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)"));
event.trigger();
}
};
grant_btn.setOnClickListener(v -> handleAction(Policy.ALLOW));
deny_btn.setOnClickListener(v -> handleAction(Policy.DENY));
suPopup.setOnClickListener((v) -> {
timer.cancel();
deny_btn.setText(getString(R.string.deny));
});
timeout.setOnTouchListener((v, event) -> {
timer.cancel();
deny_btn.setText(getString(R.string.deny));
return false;
});
timer.start();
}
@Override
public void onBackPressed() {
event.trigger();
}
@Override
public void onTrigger(CallbackHandler.Event event) {
Policy policy = (Policy) event.getResult();
String response = "socket:DENY";
if (policy != null) {
Global.Events.uidMap.remove(policy.uid);
if (policy.policy == Policy.ALLOW)
response = "socket:ALLOW";
}
try {
socket.getOutputStream().write((response).getBytes());
} catch (Exception ignored) {}
finish();
}
void handleAction(int action) {
handleAction(action, timeoutList[timeout.getSelectedItemPosition()]);
}
void handleAction(int action, int time) {
policy.policy = action;
event.trigger(policy);
if (time >= 0) {
policy.until = time == 0 ? 0 : (System.currentTimeMillis() / 1000 + time * 60);
new SuDatabaseHelper(this).addPolicy(policy);
}
}
private class SocketManager extends Async.NormalTask<Void, Void, Boolean> {
@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();
for (int i = 0; i < SU_PROTOCOL_PARAM_MAX; i++) {
int nameLen = is.readInt();
if (nameLen > SU_PROTOCOL_NAME_MAX)
throw new IllegalArgumentException("name length too long: " + nameLen);
byte[] nameBytes = new byte[nameLen];
is.readFully(nameBytes);
String name = new String(nameBytes);
if (TextUtils.equals(name, "eof"))
break;
int dataLen = is.readInt();
if (dataLen > SU_PROTOCOL_VALUE_MAX)
throw new IllegalArgumentException(name + " data length too long: " + dataLen);
byte[] dataBytes = new byte[dataLen];
is.readFully(dataBytes);
String data = new String(dataBytes);
payload.put(name, data);
}
if (payload.getAsInteger("uid") == null)
return false;
uid = payload.getAsInteger("uid");
} catch (IOException e) {
e.printStackTrace();
return false;
}
return true;
}
@Override
protected void onPostExecute(Boolean result) {
if (!result) {
finish();
return;
}
boolean showRequest = false;
event = Global.Events.uidMap.get(uid);
if (event == null) {
showRequest = true;
event = new CallbackHandler.Event() {
@Override
public void trigger(Object result) {
super.trigger(result);
CallbackHandler.unRegister(this);
}
};
Global.Events.uidMap.put(uid, event);
}
CallbackHandler.register(event, self);
try {
if (showRequest) {
policy = new Policy(uid, pm);
showRequest();
} else {
finish();
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
event.trigger();
}
}
}
}

View File

@@ -8,15 +8,12 @@ import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.provider.OpenableColumns;
import android.util.Log;
import android.widget.Toast;
import com.topjohnwu.magisk.InstallFragment;
import com.topjohnwu.magisk.MagiskHideFragment;
import com.topjohnwu.magisk.ModulesFragment;
import com.topjohnwu.magisk.Global;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.ReposFragment;
import com.topjohnwu.magisk.StatusFragment;
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import com.topjohnwu.magisk.module.ModuleHelper;
import org.json.JSONException;
import org.json.JSONObject;
@@ -60,16 +57,16 @@ public class Async {
try {
JSONObject json = new JSONObject(jsonStr);
JSONObject magisk = json.getJSONObject("magisk");
StatusFragment.remoteMagiskVersion = magisk.getDouble("versionCode");
StatusFragment.magiskLink = magisk.getString("link");
StatusFragment.releaseNoteLink = magisk.getString("note");
Global.Info.remoteMagiskVersion = magisk.getDouble("versionCode");
Global.Info.magiskLink = magisk.getString("link");
Global.Info.releaseNoteLink = magisk.getString("note");
} catch (JSONException ignored) {}
return null;
}
@Override
protected void onPostExecute(Void v) {
StatusFragment.updateCheckDone.trigger();
Global.Events.updateCheckDone.trigger();
}
}
@@ -77,8 +74,8 @@ public class Async {
new SafetyNetHelper(context) {
@Override
public void handleResults(int i) {
StatusFragment.SNCheckResult = i;
StatusFragment.safetyNetDone.trigger();
Global.Info.SNCheckResult = i;
Global.Events.safetyNetDone.trigger();
}
}.requestTest();
}
@@ -93,7 +90,7 @@ public class Async {
@Override
protected void onPostExecute(Void v) {
ModulesFragment.moduleLoadDone.trigger();
Global.Events.moduleLoadDone.trigger();
}
}
@@ -113,11 +110,11 @@ public class Async {
@Override
protected void onPostExecute(Void v) {
ReposFragment.repoLoadDone.trigger();
Global.Events.repoLoadDone.trigger();
}
}
public static class LoadApps extends RootTask<Void, Void, LoadApps.Result> {
public static class LoadApps extends RootTask<Void, Void, Void> {
private PackageManager pm;
@@ -126,33 +123,22 @@ public class Async {
}
@Override
protected Result doInBackground(Void... voids) {
List<ApplicationInfo> listApps = pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (Iterator<ApplicationInfo> i = listApps.iterator(); i.hasNext(); ) {
protected Void doInBackground(Void... voids) {
Global.Data.appList = pm.getInstalledApplications(0);
for (Iterator<ApplicationInfo> i = Global.Data.appList.iterator(); i.hasNext(); ) {
ApplicationInfo info = i.next();
if (MagiskHideFragment.BLACKLIST.contains(info.packageName) || !info.enabled)
if (ApplicationAdapter.BLACKLIST.contains(info.packageName) || !info.enabled)
i.remove();
}
Collections.sort(listApps, (a, b) -> a.loadLabel(pm).toString().toLowerCase()
Collections.sort(Global.Data.appList, (a, b) -> a.loadLabel(pm).toString().toLowerCase()
.compareTo(b.loadLabel(pm).toString().toLowerCase()));
List<String> hideList = Shell.su(Async.MAGISK_HIDE_PATH + "list");
return new Result(listApps, hideList);
Global.Data.magiskHideList = Shell.su(Async.MAGISK_HIDE_PATH + "list");
return null;
}
@Override
protected void onPostExecute(Result result) {
MagiskHideFragment.packageLoadDone.trigger(result);
}
public static class Result {
public final List<ApplicationInfo> listApps;
public final List<String> hideList;
Result(List<ApplicationInfo> listApps, List<String> hideList) {
this.listApps = listApps;
this.hideList = hideList;
}
protected void onPostExecute(Void v) {
Global.Events.packageLoadDone.trigger();
}
}
@@ -194,34 +180,36 @@ public class Async {
protected void copyToCache() throws Throwable {
publishProgress(mContext.getString(R.string.copying_msg));
try {
InputStream in = mContext.getContentResolver().openInputStream(mUri);
mCachedFile = new File(mContext.getCacheDir().getAbsolutePath() + "/install.zip");
if (mCachedFile.exists() && !mCachedFile.delete()) {
throw new IOException();
}
OutputStream outputStream = new FileOutputStream(mCachedFile);
mCachedFile = new File(mContext.getCacheDir().getAbsolutePath() + "/install.zip");
if (mCachedFile.exists() && !mCachedFile.delete()) {
Logger.error("FlashZip: Error while deleting already existing file");
throw new IOException();
}
try (
InputStream in = mContext.getContentResolver().openInputStream(mUri);
OutputStream outputStream = new FileOutputStream(mCachedFile)
) {
byte buffer[] = new byte[1024];
int length;
if (in == null) throw new FileNotFoundException();
while ((length = in.read(buffer)) > 0) {
outputStream.write(buffer, 0, length);
}
outputStream.close();
Logger.dev("FlashZip: File created successfully - " + mCachedFile.getPath());
in.close();
} catch (FileNotFoundException e) {
Log.e(Logger.TAG, "FlashZip: Invalid Uri");
Logger.error("FlashZip: Invalid Uri");
throw e;
} catch (IOException e) {
Log.e(Logger.TAG, "FlashZip: Error in creating file");
Logger.error("FlashZip: Error in creating file");
throw e;
}
}
protected boolean unzipAndCheck() {
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android");
return Utils.readFile(mCachedFile.getParent() + "/META-INF/com/google/android/updater-script")
.get(0).contains("#MAGISK");
List<String> ret;
ret = Utils.readFile(mCachedFile.getParent() + "/META-INF/com/google/android/updater-script");
return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK");
}
@Override
@@ -254,6 +242,7 @@ public class Async {
"/META-INF/com/google/android/update-binary dummy 1 " + mCachedFile.getPath(),
"if [ $? -eq 0 ]; then echo true; else echo false; fi"
);
if (!Utils.isValidShellResponse(ret)) return -1;
Logger.dev("FlashZip: Console log:");
for (String line : ret) {
Logger.dev(line);
@@ -289,33 +278,49 @@ public class Async {
}
protected void onSuccess() {
StatusFragment.updateCheckDone.trigger();
Global.Events.updateCheckDone.trigger();
new LoadModules().exec();
Utils.getAlertDialogBuilder(mContext)
.setTitle(R.string.reboot_title)
.setMessage(R.string.reboot_msg)
.setPositiveButton(R.string.reboot, (dialogInterface1, i) -> Shell.sh("su -c reboot"))
.setPositiveButton(R.string.reboot, (dialogInterface, i) -> Shell.su(true, "reboot"))
.setNegativeButton(R.string.no_thanks, null)
.show();
}
}
public static class MagiskHide extends RootTask<Object, Void, Void> {
private boolean newShell = false;
public MagiskHide() {}
public MagiskHide(boolean b) {
newShell = b;
}
@Override
protected Void doInBackground(Object... params) {
boolean add = (boolean) params[0];
String packageName = (String) params[1];
Shell.su(MAGISK_HIDE_PATH + (add ? "add " : "rm ") + packageName);
String command = (String) params[0];
Shell.su(newShell, MAGISK_HIDE_PATH + command);
return null;
}
public void add(CharSequence packageName) {
exec(true, packageName);
exec("add " + packageName);
}
public void rm(CharSequence packageName) {
exec(false, packageName);
exec("rm " + packageName);
}
public void enable() {
exec("enable");
}
public void disable() {
exec("disable");
}
}
@@ -324,17 +329,16 @@ public class Async {
@Override
protected Void doInBackground(Void... params) {
if (Shell.rootAccess()) {
InstallFragment.blockList = Shell.su("ls /dev/block | grep mmc");
if (InstallFragment.bootBlock == null) {
InstallFragment.bootBlock = Utils.detectBootImage();
}
Global.Data.blockList = Shell.su("ls /dev/block | grep mmc");
if (Global.Info.bootBlock == null)
Global.Info.bootBlock = Utils.detectBootImage();
}
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
InstallFragment.blockDetectionDone.trigger();
protected void onPostExecute(Void v) {
Global.Events.blockDetectionDone.trigger();
}
}
}

View File

@@ -0,0 +1,18 @@
package com.topjohnwu.magisk.utils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
public class ByteArrayInOutStream extends ByteArrayOutputStream {
public ByteArrayInputStream getInputStream() {
ByteArrayInputStream in = new ByteArrayInputStream(buf, 0, count);
count = 0;
buf = new byte[32];
return in;
}
public void setBuffer(byte[] buffer) {
buf = buffer;
count = buffer.length;
}
}

View File

@@ -2,13 +2,15 @@ package com.topjohnwu.magisk.utils;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class CallbackHandler {
private static HashMap<Event, HashSet<EventListener>> listeners = new HashMap<>();
private static Map<Event, Set<EventListener>> listeners = new HashMap<>();
public static void register(Event event, EventListener listener) {
HashSet<EventListener> list = listeners.get(event);
Set<EventListener> list = listeners.get(event);
if (list == null) {
list = new HashSet<>();
listeners.put(event, list);
@@ -16,15 +18,22 @@ public class CallbackHandler {
list.add(listener);
}
public static void unRegister(Event event) {
Set<EventListener> list = listeners.remove(event);
if (list != null) {
list.clear();
}
}
public static void unRegister(Event event, EventListener listener) {
HashSet<EventListener> list = listeners.get(event);
Set<EventListener> list = listeners.get(event);
if (list != null) {
list.remove(listener);
}
}
private static void triggerCallback(Event event) {
HashSet<EventListener> list = listeners.get(event);
Set<EventListener> list = listeners.get(event);
if (list != null) {
for (EventListener listener : list) {
listener.onTrigger(event);

View File

@@ -1,4 +1,4 @@
package com.topjohnwu.magisk;
package com.topjohnwu.magisk.utils;
import android.content.Context;
@@ -23,9 +23,10 @@ import java.util.List;
* FabMenu in a Coordinator Layout.
*
* Remember to use the correct namespace for the fab:
* xmlns:fab="http://schemas.android.com/apk/res-auto"
* xmlns:app="http://schemas.android.com/apk/res-auto"
*/
public class FABBehavior extends CoordinatorLayout.Behavior {
public class FABBehavior extends CoordinatorLayout.Behavior<View> {
private float mTranslationY;
public FABBehavior(Context context, AttributeSet attrs) {
@@ -74,11 +75,11 @@ public class FABBehavior extends CoordinatorLayout.Behavior {
private float getTranslationY(CoordinatorLayout parent, View child) {
float minOffset = 0.0F;
List dependencies = parent.getDependencies(child);
List<View> dependencies = parent.getDependencies(child);
int i = 0;
for (int z = dependencies.size(); i < z; ++i) {
View view = (View) dependencies.get(i);
View view = dependencies.get(i);
if (view instanceof Snackbar.SnackbarLayout && parent.doViewsOverlap(child, view)) {
minOffset = Math.min(minOffset, ViewCompat.getTranslationY(view) - (float) view.getHeight());
}

View File

@@ -2,36 +2,38 @@ package com.topjohnwu.magisk.utils;
import android.util.Log;
import com.topjohnwu.magisk.Global;
public class Logger {
public static final String TAG = "Magisk";
public static final String DEV_TAG = "Magisk: DEV";
public static final String DEBUG_TAG = "Magisk: DEBUG";
public static boolean logShell, devLog;
public static void debug(String msg) {
Log.d(DEBUG_TAG, msg);
Log.d(TAG, "DEBUG: " + msg);
}
public static void error(String msg) {
Log.e(TAG, "ERROR: " + msg);
}
public static void dev(String msg, Object... args) {
if (devLog) {
if (Global.Configs.devLogging) {
if (args.length == 1 && args[0] instanceof Throwable) {
Log.d(DEV_TAG, msg, (Throwable) args[0]);
Log.d(TAG, "DEV: " + msg, (Throwable) args[0]);
} else {
Log.d(DEV_TAG, String.format(msg, args));
Log.d(TAG, "DEV: " + String.format(msg, args));
}
}
}
public static void dev(String msg) {
if (devLog) {
Log.d(DEV_TAG, msg);
if (Global.Configs.devLogging) {
Log.d(TAG, "DEBUG: " + msg);
}
}
public static void shell(boolean root, String msg) {
if (logShell) {
if (Global.Configs.shellLogging) {
Log.d(root ? "SU" : "SH", msg);
}
}

View File

@@ -15,16 +15,15 @@ public class Shell {
// -1 = problematic/unknown issue; 0 = not rooted; 1 = properly rooted
public static int rootStatus;
private static boolean isInit = false;
private static Process rootShell;
private static DataOutputStream rootSTDIN;
private static StreamGobbler rootSTDOUT;
private static List<String> rootOutList = new ArrayList<>();
private static List<String> rootOutList = Collections.synchronizedList(new ArrayList<String>());
static {
init();
}
public static void init() {
private static void init() {
isInit = true;
try {
rootShell = Runtime.getRuntime().exec("su");
@@ -41,7 +40,7 @@ public class Shell {
// Setup umask and PATH
su("umask 022");
su("PATH=/data/busybox:$PATH");
su("PATH=`[ -e /dev/busybox ] && echo /dev/busybox || echo /data/busybox`:$PATH");
List<String> ret = su("echo -BOC-", "id");
@@ -64,7 +63,7 @@ public class Shell {
}
public static boolean rootAccess() {
return rootStatus > 0;
return isInit && rootStatus > 0;
}
public static List<String> sh(String... commands) {
@@ -120,9 +119,12 @@ public class Shell {
DataOutputStream STDIN;
StreamGobbler STDOUT;
if (!rootAccess()) {
// Create the default shell if not init
if (!newShell && !isInit)
init();
if (!newShell && !rootAccess())
return null;
}
if (newShell) {
res = Collections.synchronizedList(new ArrayList<String>());
@@ -170,6 +172,7 @@ public class Shell {
try {
// Process terminated, it means the interactive shell has some issues
process.exitValue();
rootStatus = -1;
return null;
} catch (IllegalThreadStateException e) {
// Process still running, gobble output until done
@@ -183,17 +186,22 @@ public class Shell {
break;
}
}
try { STDOUT.join(100); } catch (InterruptedException err) { return null; }
try { STDOUT.join(100); } catch (InterruptedException err) {
rootStatus = -1;
return null;
}
}
}
}
} catch (IOException e) {
if (!e.getMessage().contains("EPIPE")) {
Logger.dev("Shell: Root shell error...");
rootStatus = -1;
return null;
}
} catch(InterruptedException e) {
Logger.dev("Shell: Root shell error...");
rootStatus = -1;
return null;
}

View File

@@ -5,6 +5,7 @@ import android.app.AlertDialog;
import android.app.DownloadManager;
import android.content.Context;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Environment;
@@ -12,55 +13,45 @@ import android.support.v4.app.ActivityCompat;
import android.text.TextUtils;
import android.widget.Toast;
import com.topjohnwu.magisk.Global;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.receivers.DownloadReceiver;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.List;
public class Utils {
public static boolean isDownloading = false;
public static boolean isDarkTheme;
public static boolean itemExist(String path) {
return itemExist(true, path);
}
public static boolean itemExist(boolean root, String path) {
String command = "if [ -e " + path + " ]; then echo true; else echo false; fi";
if (Shell.rootAccess() && root) {
return Boolean.parseBoolean(Shell.su(command).get(0));
} else {
return new File(path).exists();
}
List<String> ret = Shell.su(command);
return isValidShellResponse(ret) && Boolean.parseBoolean(ret.get(0));
}
public static boolean commandExists(String s) {
List<String> ret;
String command = "if [ -z $(which " + s + ") ]; then echo false; else echo true; fi";
ret = Shell.sh(command);
return Boolean.parseBoolean(ret.get(0));
List<String> ret = Shell.sh(command);
return isValidShellResponse(ret) && Boolean.parseBoolean(ret.get(0));
}
public static boolean createFile(String path) {
String folder = path.substring(0, path.lastIndexOf('/'));
String command = "mkdir -p " + folder + " 2>/dev/null; touch " + path + " 2>/dev/null; if [ -f \"" + path + "\" ]; then echo true; else echo false; fi";
return Shell.rootAccess() && Boolean.parseBoolean(Shell.su(command).get(0));
List<String> ret = Shell.su(command);
return isValidShellResponse(ret) && Boolean.parseBoolean(ret.get(0));
}
public static boolean removeItem(String path) {
String command = "rm -rf " + path + " 2>/dev/null; if [ -e " + path + " ]; then echo false; else echo true; fi";
return Shell.rootAccess() && Boolean.parseBoolean(Shell.su(command).get(0));
List<String> ret = Shell.su(command);
return isValidShellResponse(ret) && Boolean.parseBoolean(ret.get(0));
}
public static List<String> getModList(String path) {
List<String> ret;
String command = "find " + path + " -type d -maxdepth 1 ! -name \"*.core\" ! -name \"*lost+found\" ! -name \"*magisk\"";
ret = Shell.su(command);
return ret;
return Shell.su(command);
}
public static List<String> readFile(String path) {
@@ -118,14 +109,13 @@ public class Utils {
"echo \"${BOOTIMAGE##*/}\""
};
List<String> ret = Shell.su(commands);
if (!ret.isEmpty()) {
if (isValidShellResponse(ret))
return ret.get(0);
}
return null;
}
public static AlertDialog.Builder getAlertDialogBuilder(Context context) {
if (isDarkTheme) {
if (Global.Configs.isDarkTheme) {
return new AlertDialog.Builder(context, R.style.AlertDialog_dh);
} else {
return new AlertDialog.Builder(context);
@@ -136,17 +126,27 @@ public class Utils {
return !TextUtils.isEmpty(string) && string.toString().toLowerCase().contains(nonNullLowercaseSearch);
}
public static class ByteArrayInOutStream extends ByteArrayOutputStream {
public ByteArrayInputStream getInputStream() {
ByteArrayInputStream in = new ByteArrayInputStream(buf, 0, count);
count = 0;
buf = new byte[32];
return in;
}
public void setBuffer(byte[] buffer) {
buf = buffer;
count = buffer.length;
public static boolean isValidShellResponse(List<String> list) {
if (list != null && list.size() != 0) {
// Check if all empty
for (String res : list) {
if (!TextUtils.isEmpty(res)) return true;
}
}
return false;
}
public static int getPrefsInt(SharedPreferences prefs, String key, int def) {
return Integer.parseInt(prefs.getString(key, String.valueOf(def)));
}
public static void checkAndStartMagiskHide() {
String command = "ps | grep magiskhide >/dev/null; echo $?";
List<String> ret = Shell.su(command);
if (!isValidShellResponse(ret))
return;
if (Integer.parseInt(ret.get(0)) != 0)
new Async.MagiskHide().enable();
}
}

View File

@@ -0,0 +1,43 @@
package com.topjohnwu.magisk.utils;
import android.support.annotation.NonNull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ValueSortedMap<K, V extends Comparable<? super V>> extends HashMap<K, V> {
private List<V> sorted = new ArrayList<>();
@NonNull
@Override
public Collection<V> values() {
if (sorted.isEmpty()) {
sorted.addAll(super.values());
Collections.sort(sorted);
}
return sorted;
}
@Override
public V put(K key, V value) {
sorted.clear();
return super.put(key, value);
}
@Override
public void putAll(Map<? extends K, ? extends V> m) {
sorted.clear();
super.putAll(m);
}
@Override
public V remove(Object key) {
sorted.clear();
return super.remove(key);
}
}

View File

@@ -3,8 +3,6 @@ package com.topjohnwu.magisk.utils;
import android.content.Context;
import android.util.Pair;
import com.topjohnwu.magisk.utils.Utils.ByteArrayInOutStream;
import org.spongycastle.asn1.ASN1InputStream;
import org.spongycastle.asn1.ASN1ObjectIdentifier;
import org.spongycastle.asn1.DEROutputStream;
@@ -125,17 +123,16 @@ public class ZipUtils {
}
public static void unzip(File file, File folder, String path) {
try {
int count;
FileOutputStream out;
File dest;
InputStream is;
JarEntry entry;
byte data[] = new byte[4096];
JarFile zipfile = new JarFile(file);
Enumeration e = zipfile.entries();
int count;
FileOutputStream out;
File dest;
InputStream is;
JarEntry entry;
byte data[] = new byte[4096];
try (JarFile zipfile = new JarFile(file)) {
Enumeration<JarEntry> e = zipfile.entries();
while(e.hasMoreElements()) {
entry = (JarEntry) e.nextElement();
entry = e.nextElement();
if (!entry.getName().contains(path)
|| entry.getName().charAt(entry.getName().length() - 1) == '/') {
// Ignore directories, only create files
@@ -175,8 +172,7 @@ public class ZipUtils {
inputJar = new JarMap(new JarInputStream(inputStream));
if (signWholeFile) {
if (!"RSA".equalsIgnoreCase(privateKey.getAlgorithm())) {
System.err.println("Cannot sign OTA packages with non-RSA keys");
System.exit(1);
throw new IOException("Cannot sign OTA packages with non-RSA keys");
}
signWholeFile(inputJar, context.getAssets().open(PUBLIC_KEY_NAME),
publicKey, privateKey, outputStream);
@@ -518,9 +514,10 @@ public class ZipUtils {
.build(signer, publicKey));
gen.addCertificates(certs);
CMSSignedData sigData = gen.generate(data, false);
ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded());
DEROutputStream dos = new DEROutputStream(out);
dos.writeObject(asn1.readObject());
try (ASN1InputStream asn1 = new ASN1InputStream(sigData.getEncoded())) {
DEROutputStream dos = new DEROutputStream(out);
dos.writeObject(asn1.readObject());
}
}
/**
* Copy all the files in a manifest from input to output. We set

View File

@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.6)
add_library(zipadjust SHARED
jni_glue.c
zipadjust.c)
find_library(libz z)
find_library(liblog log)
target_link_libraries(zipadjust ${libz} ${liblog})

View File

@@ -0,0 +1,23 @@
//
// Java entry point
//
#include <jni.h>
#include <stdlib.h>
#include "zipadjust.h"
JNIEXPORT jbyteArray JNICALL
Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust(JNIEnv *env, jclass type, jbyteArray jbytes, jint size) {
fin = (*env)->GetPrimitiveArrayCritical(env, jbytes, NULL);
insize = (size_t) size;
zipadjust(0);
(*env)->ReleasePrimitiveArrayCritical(env, jbytes, fin, 0);
jbyteArray ret = (*env)->NewByteArray(env, outsize);
(*env)->SetByteArrayRegion(env, ret, 0, outsize, (const jbyte*) fout);
free(fout);
return ret;
}

View File

@@ -1,35 +1,16 @@
#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <zlib.h>
#include <android/log.h>
#include "zipadjust.h"
#define LOG_TAG "zipadjust"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
size_t insize, outsize = 0, alloc = 0;
unsigned char *fin, *fout;
int zipadjust(int decompress) ;
JNIEXPORT jbyteArray JNICALL
Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust(JNIEnv *env, jclass type, jbyteArray jbytes,
jint size) {
fin = (*env)->GetPrimitiveArrayCritical(env, jbytes, NULL);
insize = (size_t) size;
zipadjust(0);
(*env)->ReleasePrimitiveArrayCritical(env, jbytes, fin, 0);
jbyteArray ret = (*env)->NewByteArray(env, outsize);
(*env)->SetByteArrayRegion(env, ret, 0, outsize, (const jbyte*) fout);
free(fout);
return ret;
}
size_t insize = 0, outsize = 0, alloc = 0;
unsigned char *fin = NULL, *fout = NULL;
#pragma pack(1)
struct local_header_struct {

View File

@@ -0,0 +1,9 @@
#ifndef MAGISKMANAGER_ZIPADJUST_H_H
#define MAGISKMANAGER_ZIPADJUST_H_H
int zipadjust(int decompress);
extern size_t insize, outsize, alloc;
extern unsigned char *fin, *fout;
#endif //MAGISKMANAGER_ZIPADJUST_H_H

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24"
android:width="24dp">
<path
android:fillColor="#000000"
android:pathData="M7.41 7.84L12 12.42l4.59-4.58L18 9.25l-6 6-6-6z" />
<path
android:pathData="M0-.75h24v24H0z" />
</vector>

View File

@@ -4,6 +4,6 @@
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#757575"
android:fillColor="#000"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#000"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M6,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM12,10c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M7.58,4.08L6.15,2.65C3.75,4.48 2.17,7.3 2.03,10.5h2c0.15,-2.65 1.51,-4.97 3.55,-6.42zM19.97,10.5h2c-0.15,-3.2 -1.73,-6.02 -4.12,-7.85l-1.42,1.43c2.02,1.45 3.39,3.77 3.54,6.42zM18,11c0,-3.07 -1.64,-5.64 -4.5,-6.32L13.5,4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2v-5zM12,22c0.14,0 0.27,-0.01 0.4,-0.04 0.65,-0.14 1.18,-0.58 1.44,-1.18 0.1,-0.24 0.15,-0.5 0.15,-0.78h-4c0.01,1.1 0.9,2 2.01,2z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FFd32f2f"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-2h2v2zM13,13h-2L11,7h2v6z"/>
</vector>

View File

@@ -0,0 +1,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M5.41,21L6.12,17H2.12L2.47,15H6.47L7.53,9H3.53L3.88,7H7.88L8.59,3H10.59L9.88,7H15.88L16.59,3H18.59L17.88,7H21.88L21.53,9H17.53L16.47,15H20.47L20.12,17H16.12L15.41,21H13.41L14.12,17H8.12L7.41,21H5.41M9.53,9L8.47,15H14.47L15.53,9H9.53Z" />
</vector>

View File

@@ -7,7 +7,6 @@
<include layout="@layout/toolbar"/>
<ScrollView
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:layout_width="fill_parent"

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:minWidth="350dp"
android:layout_gravity="center"
android:orientation="vertical">
<TextView
android:text="@string/su_request_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="@+id/request_title"
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal"
android:layout_marginTop="20dp"
android:layout_marginBottom="5dp"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:layout_gravity="center_horizontal">
<ImageView
android:id="@+id/app_icon"
android:layout_weight="0"
android:layout_marginEnd="10dp"
android:layout_height="50dp"
android:layout_width="50dp"
android:layout_marginStart="5dp"
android:layout_gravity="center_vertical" />
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_weight="1"
android:layout_gravity="center_vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="200dp"
android:maxWidth="300dp"
android:maxLines="1"
android:ellipsize="end"
android:textColor="?android:textColorPrimary"
android:textAppearance="?android:attr/textAppearanceMedium"
android:id="@+id/app_name" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:minWidth="200dp"
android:maxWidth="300dp"
android:maxLines="1"
android:ellipsize="end"
android:textColor="?android:textColorSecondary"
android:id="@+id/package_name" />
</LinearLayout>
</LinearLayout>
<Spinner
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/timeout"
android:layout_gravity="center_horizontal" />
<TextView
android:id="@+id/warning"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableStart="@drawable/ic_su_warning"
android:textColor="?android:textColorSecondary"
android:text="@string/su_warning"
android:layout_gravity="center_horizontal"
android:layout_margin="5dp"
android:drawablePadding="10dp" />
<LinearLayout
style="?android:buttonBarStyle"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="bottom"
android:paddingLeft="30dp"
android:paddingRight="30dp">
<Button
style="?android:buttonBarButtonStyle"
android:text="@string/deny_with_str"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/deny_btn"
android:layout_weight="1" />
<Button
style="?android:buttonBarButtonStyle"
android:text="@string/grant"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/grant_btn"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,251 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:id="@+id/install_info_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp">
<TextView
android:id="@+id/install_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="5dp"
android:textStyle="bold" />
<TextView
android:id="@+id/current_version_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="5dp"
android:textStyle="bold" />
</LinearLayout>
</android.support.v7.widget.CardView>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:maxWidth="400dp"
android:minWidth="400dp">
<android.support.v7.widget.CardView
android:id="@+id/bootimage_card"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true"
android:layout_width="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:paddingBottom="10dp"
android:text="@string/boot_image_title"
android:textStyle="bold" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="15dp"
android:layout_marginStart="15dp">
<Spinner
android:layout_width="0dp"
android:layout_height="match_parent"
android:id="@+id/block_spinner"
android:layout_weight="1" />
<Button
android:text="@string/detect_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/detect_bootimage"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/install_option_card"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true"
android:layout_width="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:paddingBottom="10dp"
android:text="@string/advanced_settings_title"
android:textStyle="bold" />
<CheckBox
android:text="@string/keep_force_encryption"
android:id="@+id/keep_force_enc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="25dp"
android:layout_marginStart="25dp" />
<CheckBox
android:text="@string/keep_dm_verity"
android:id="@+id/keep_verity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="25dp"
android:layout_marginStart="25dp" />
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/flash_button"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true"
android:layout_width="match_parent"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginEnd="25dp"
android:layout_marginStart="25dp">
<ImageView
android:layout_width="50dp"
android:layout_height="wrap_content"
app:srcCompat="@mipmap/ic_launcher"
android:id="@+id/imageView"
android:layout_weight="0"
android:layout_marginEnd="20dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/magiskify"
android:ems="10"
android:gravity="center"
android:layout_weight="1"
android:textStyle="bold"
android:textAllCaps="false"
android:textSize="20sp"
android:fontFamily="sans-serif" />
<FrameLayout
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_weight="0"
android:layout_marginStart="20dp">
</FrameLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/uninstall_button"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true"
android:layout_width="match_parent"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/uninstall"
android:ems="10"
android:gravity="center"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:textStyle="bold"
android:textAllCaps="false"
android:textSize="20sp"
android:fontFamily="sans-serif" />
</android.support.v7.widget.CardView>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -0,0 +1,28 @@
<LinearLayout
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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_marginTop="?attr/actionBarSize"
tools:context="com.topjohnwu.magisk.LogFragment">
<android.support.design.widget.TabLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/colorPrimary"
android:id="@+id/tab"
app:tabPaddingEnd="20dp"
app:tabPaddingStart="20dp"
android:elevation="4dp"
android:visibility="gone">
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="@+id/container"/>
</LinearLayout>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.SwipeRefreshLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:fab="http://schemas.android.com/apk/res-auto"
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
@@ -11,8 +10,6 @@
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinator"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -25,7 +22,7 @@
android:layout_height="match_parent"
android:clipToPadding="false"
android:dividerHeight="@dimen/card_divider_space"
app:layoutManager="android.support.v7.widget.LinearLayoutManager" />
fab:layoutManager="android.support.v7.widget.LinearLayoutManager" />
<TextView
android:id="@+id/empty_rv"
@@ -47,7 +44,7 @@
android:layout_gravity="bottom|center_horizontal"
android:layout_marginEnd="113dp"
android:layout_marginBottom="10dp"
app:layout_behavior=".FABBehavior"
fab:layout_behavior=".utils.FABBehavior"
fab:menu_fab_size="normal"
fab:menu_showShadow="true"
fab:menu_shadowColor="#66000000"

View File

@@ -8,8 +8,7 @@
android:layout_marginTop="?attr/actionBarSize"
android:orientation="vertical">
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="8dp"

View File

@@ -0,0 +1,28 @@
<LinearLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.topjohnwu.magisk.SuLogFragment">
<TextView
android:id="@+id/empty_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:fontFamily="sans-serif-light"
android:gravity="center"
android:text="@string/log_is_empty"
android:textSize="20sp"
android:textStyle="italic"
android:visibility="gone" />
<android.support.v7.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" />
</LinearLayout>

View File

@@ -0,0 +1,28 @@
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_marginTop="?attr/actionBarSize"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/empty_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:fontFamily="sans-serif-light"
android:gravity="center"
android:text="@string/no_apps_found"
android:textSize="20sp"
android:textStyle="italic"
android:visibility="gone" />
<android.support.v7.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" />
</LinearLayout>

View File

@@ -1,213 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_marginTop="?attr/actionBarSize"
android:orientation="vertical">
<android.support.v7.widget.CardView
android:id="@+id/install_info_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp">
<TextView
android:id="@+id/install_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="5dp"
android:textStyle="bold" />
<TextView
android:id="@+id/current_version_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:padding="5dp"
android:textStyle="bold" />
</LinearLayout>
</android.support.v7.widget.CardView>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:maxWidth="400dp"
android:minWidth="400dp">
<android.support.v7.widget.CardView
android:id="@+id/bootimage_card"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true"
android:layout_width="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:paddingBottom="10dp"
android:text="@string/boot_image_title"
android:textStyle="bold" />
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="15dp"
android:layout_marginStart="15dp">
<Spinner
android:layout_width="0dp"
android:layout_height="match_parent"
android:id="@+id/block_spinner"
android:layout_weight="1" />
<Button
android:text="@string/detect_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/detect_bootimage"/>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/install_option_card"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true"
android:layout_width="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="15dp"
android:layout_marginBottom="15dp">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:paddingBottom="10dp"
android:text="@string/advanced_settings_title"
android:textStyle="bold" />
<CheckBox
android:text="@string/keep_force_encryption"
android:id="@+id/keep_force_enc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="25dp"
android:layout_marginStart="25dp" />
<CheckBox
android:text="@string/keep_dm_verity"
android:id="@+id/keep_verity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="25dp"
android:layout_marginStart="25dp" />
</LinearLayout>
</android.support.v7.widget.CardView>
<android.support.v7.widget.CardView
android:id="@+id/flash_button"
android:layout_gravity="center"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="6dp"
style="?attr/cardStyle"
app:cardUseCompatPadding="true"
android:layout_width="match_parent"
android:foreground="?android:attr/selectableItemBackground"
android:clickable="true">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_marginEnd="25dp"
android:layout_marginStart="25dp">
<ImageView
android:layout_width="50dp"
android:layout_height="wrap_content"
app:srcCompat="@mipmap/ic_launcher"
android:id="@+id/imageView"
android:layout_weight="0"
android:layout_marginEnd="20dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="@string/magiskify"
android:ems="10"
android:gravity="center"
android:layout_weight="1"
android:textStyle="bold"
android:textAllCaps="false"
android:textSize="20sp"
android:fontFamily="sans-serif" />
<FrameLayout
android:layout_width="50dp"
android:layout_height="match_parent"
android:layout_weight="0"
android:layout_marginStart="20dp">
</FrameLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
</LinearLayout>

View File

@@ -22,7 +22,6 @@
android:padding="@dimen/card_layout_padding">
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/app_icon"
android:layout_width="@dimen/card_appicon_size"
android:layout_height="@dimen/card_appicon_size"

View File

@@ -29,7 +29,6 @@
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginTop="0dp"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="false"/>
@@ -40,7 +39,6 @@
android:layout_alignParentStart="true"
android:layout_below="@id/title"
android:textAppearance="?android:attr/textAppearanceSmall"
android:text="@string/no_info_provided"
android:textColor="@android:color/tertiary_text_dark"
android:textIsSelectable="false"
android:textStyle="bold|italic"/>
@@ -51,7 +49,6 @@
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_below="@id/version_name"
android:text="@string/no_info_provided"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/tertiary_text_dark"
android:textIsSelectable="false"
@@ -63,7 +60,6 @@
android:layout_height="wrap_content"
android:layout_below="@id/author"
android:layout_gravity="center_vertical"
android:text="@string/no_info_provided"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textIsSelectable="false"/>
@@ -106,6 +102,7 @@
android:gravity="center"
android:padding="@dimen/checkbox_padding"
android:src="@drawable/ic_delete"
android:tint="@color/icon_grey"
tools:ignore="ContentDescription"/>
</LinearLayout>

View File

@@ -0,0 +1,173 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
style="?attr/cardStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="@dimen/card_vertical_margin"
android:layout_marginEnd="@dimen/card_horizontal_margin"
android:layout_marginStart="@dimen/card_horizontal_margin"
android:layout_marginTop="@dimen/card_vertical_margin"
android:minHeight="?android:attr/listPreferredItemHeight"
card_view:cardCornerRadius="@dimen/card_corner_radius"
card_view:cardElevation="@dimen/card_elevation">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/info_layout"
android:paddingStart="10dp"
android:paddingEnd="10dp"
android:paddingTop="5dp"
android:paddingBottom="5dp">
<ImageView
android:id="@+id/app_icon"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_vertical"
android:gravity="end" />
<LinearLayout
android:orientation="vertical"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp">
<TextView
android:id="@+id/app_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="?android:attr/textColorPrimary"
android:textIsSelectable="false"/>
<TextView
android:id="@+id/package_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/tertiary_text_dark"
android:textIsSelectable="false" />
</LinearLayout>
<Switch
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/master_switch"
android:layout_weight="0"
android:checked="false"
android:layout_gravity="center_vertical"
android:gravity="center_vertical" />
</LinearLayout>
<LinearLayout
android:id="@+id/expand_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="horizontal"
android:paddingStart="10dp"
android:paddingEnd="10dp">
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_notifications"
android:layout_gravity="center_vertical"
android:tint="@color/icon_grey"
android:layout_marginEnd="10dp" />
<Switch
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/notification_switch"
android:checked="false"
android:layout_gravity="center_vertical"
android:gravity="center_vertical" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:layout_gravity="center">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_bug_report"
android:layout_gravity="center_vertical"
android:tint="@color/icon_grey"
android:layout_marginEnd="10dp" />
<Switch
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/logging_switch"
android:checked="false"
android:layout_gravity="center_vertical"
android:gravity="center_vertical" />
</LinearLayout>
<ImageView
android:id="@+id/delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_delete"
android:tint="@color/icon_grey"
android:layout_gravity="center" />
<ImageView
android:id="@+id/more_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_more"
android:tint="@color/icon_grey"
android:layout_gravity="center" />
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

View File

@@ -43,7 +43,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:maxLines="1"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textIsSelectable="false"/>

View File

@@ -0,0 +1,145 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
style="?attr/cardStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="2dp"
android:layout_marginEnd="@dimen/card_horizontal_margin"
android:layout_marginStart="@dimen/card_horizontal_margin"
android:layout_marginTop="2dp"
card_view:cardCornerRadius="@dimen/card_corner_radius"
card_view:cardElevation="@dimen/card_elevation">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/info_layout"
android:padding="10dp">
<TextView
android:id="@+id/app_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:ellipsize="end"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorPrimary"
android:textIsSelectable="false"
android:layout_weight="2" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/action"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:gravity="center_horizontal" />
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:id="@+id/time"
android:layout_weight="1"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:gravity="center_horizontal" />
</LinearLayout>
<LinearLayout
android:id="@+id/expand_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:gravity="center"
android:layout_gravity="center_horizontal">
<LinearLayout
android:orientation="horizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_weight="1">
<TextView
android:text="@string/pid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/fromPid"
android:textColor="?android:attr/textColorSecondary"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_weight="1">
<TextView
android:text="@string/target_uid"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/toUid"
android:textColor="?android:attr/textColorSecondary"
android:textSize="12sp" />
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_weight="2">
<TextView
android:text="@string/command"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"
android:textSize="12sp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/command"
android:textColor="?android:attr/textColorSecondary"
android:gravity="start"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="5dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="25dp"
android:src="@drawable/ic_arrow"
android:id="@+id/arrow"
android:layout_marginEnd="10dp" />
<TextView
android:text="2017/1/1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/date"
android:textColor="?android:attr/textColorPrimary"
android:textAppearance="?android:attr/textAppearanceSmall"
android:gravity="center_vertical" />
</LinearLayout>

View File

@@ -16,6 +16,18 @@
android:title="@string/install"
android:visible="false"/>
<item
android:id="@+id/superuser"
android:icon="@drawable/ic_superuser"
android:title="@string/superuser"
android:visible="false"/>
</group>
<group
android:checkableBehavior="single"
android:id="@+id/second_group">
<item
android:id="@+id/modules"
android:icon="@drawable/ic_extension"
@@ -30,15 +42,17 @@
android:id="@+id/magiskhide"
android:icon="@drawable/ic_autoroot"
android:title="@string/magiskhide"/>
<item
android:id="@+id/log"
android:icon="@drawable/ic_bug_report"
android:title="@string/log"/>
</group>
<group
android:checkableBehavior="none"
android:id="@+id/second_group">
android:id="@+id/third_group">
<item
android:id="@+id/settings"

View File

@@ -8,15 +8,9 @@
android:title="@string/menuSaveToSd"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_send"
android:icon="@drawable/ic_send"
android:title="@string/menuSend"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/menu_clear"
android:icon="@drawable/ic_delete_black"
android:icon="@drawable/ic_delete"
android:title="@string/menuClearLog"
app:showAsAction="ifRoom"/>

View File

@@ -39,7 +39,6 @@
<!--Log Fragment-->
<string name="menuSaveToSd">حفظ إلى بطاقة ذاكرة SD</string>
<string name="menuSend">إرسال</string>
<string name="menuReload">إعادة تحميل</string>
<string name="menuClearLog">حذف السجل الآن</string>
<string name="logs_cleared">تم حذف السجل بنجاح</string>
@@ -83,12 +82,9 @@
<!--Settings Activity -->
<string name="settings_general_category">عام</string>
<string name="settings_theme_title">السمة</string>
<string name="settings_theme_summary">أختر سمة</string>
<string name="theme_default">إفتراضي</string>
<string name="theme_dark">غامق</string>
<string name="settings_dark_theme_title">السمة</string>
<string name="settings_dark_theme_summary">أختر سمة</string>
<string name="settings_magiskhide_title">تمكين إخفاء Magisk</string>
<string name="settings_magiskhide_summary">إخفاء Magisk من مختلف حالات الإكتشاف</string>
<string name="settings_busybox_title">تمكين BusyBox</string>
<string name="settings_busybox_summary">ربط تحميل Magisk\'s المدمج في busybox إلى xbin</string>

View File

@@ -1,23 +1,51 @@
<resources>
<!--Universal-->
<!--Welcome Activity-->
<string name="navigation_drawer_open">Navigationsleiste öffnen</string>
<string name="navigation_drawer_close">Navigationsleiste schließen</string>
<string name="navigation_drawer_open">Navigationsmenü öffnen</string>
<string name="navigation_drawer_close">Navigationsmenü schließen</string>
<string name="magiskhide">Magisk Hide</string>
<string name="modules">Module</string>
<string name="downloads">Downloads</string>
<string name="downloads">Download</string>
<string name="superuser">Superuser</string>
<string name="log">Log</string>
<string name="settings">Einstellungen</string>
<string name="status">Übersicht</string>
<string name="install">Installieren</string>
<!--Magisk Fragment-->
<string name="magisk_version">Magisk v%1$s installiert</string>
<string name="magisk_version_error">Haben Sie Magisk installiert?</string>
<!--Status Fragment-->
<string name="magisk_version">Magisk v%1$s ist installiert</string>
<string name="magisk_version_disable">Magisk v%1$s ist deaktiviert</string>
<string name="magisk_version_error">Magisk ist nicht installiert</string>
<string name="magisk_update_available">Magisk v%1$.1f update!</string>
<string name="cannot_check_updates">Kann nicht auf Aktualisierungen prüfen</string>
<string name="up_to_date">Aktuellste Version von %1$s installiert</string>
<string name="checking_for_updates">Suche nach Updates…</string>
<string name="magisk_update_available">Magisk v%1$.1f ist verfügbar!</string>
<string name="cannot_check_updates">Updatesuche fehlgeschlagen.\nIst eine Internetverbindung verfügbar?</string>
<string name="up_to_date">Die neuste Version von %1$s ist bereits installiert</string>
<string name="root_error">Gerootet, aber keine root-Rechte. Wurde der root-Zugriff verweigert?</string>
<string name="not_rooted">Nicht gerootet</string>
<string name="proper_root">Ordnungsgemäß gerootet</string>
<string name="safetyNet_check_text">SafetyNet-Status abfragen</string>
<string name="checking_safetyNet_status">Prüfe SafetyNet-Status…</string>
<string name="safetyNet_connection_failed">Verbindung zur Google-API fehlgeschlagen</string>
<string name="safetyNet_connection_suspended">Verbindung zur Google-API wurde ausgesetzt</string>
<string name="safetyNet_error">SafetyNet-Status konnte nicht geprüft werden. Ist eine Internetverbindung verfügbar?</string>
<string name="safetyNet_fail">SafetyNet nicht bestanden: \"CTS profile mismatch\"</string>
<string name="safetyNet_pass">SafetyNet bestanden</string>
<string name="root_info_warning">Funktionalität stark eingeschränkt</string>
<!--Install Fragment-->
<string name="auto_detect">"%1$s (autom.)"</string>
<string name="boot_image_title">Boot-Image-Pfad</string>
<string name="detect_button">Automatisch</string>
<string name="advanced_settings_title">Erweiterte Einstellungen</string>
<string name="keep_force_encryption">\"force encryption\" beibehalten</string>
<string name="keep_dm_verity">\"dm-verity\"-Test beibehalten</string>
<string name="current_magisk_title">Installierte Magisk-Version: v%1$s</string>
<string name="install_magisk_title">Neuste Magisk-Version: v%1$.1f</string>
<!--Root Fragment-->
<!--Module Fragment-->
<string name="no_info_provided">(Nichts angegeben)</string>
@@ -28,78 +56,137 @@
<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="fab_flash_zip">Modul aus Zip-Datei flashen</string>
<!--Repo Fragment-->
<string name="update_available">Aktualisierung verfügbar</string>
<string name="update_available">Update verfügbar</string>
<string name="installed">Installiert</string>
<string name="not_installed">Nicht Installiert</string>
<string name="changelog">Changelog</string>
<string name="not_installed">Nicht installiert</string>
<string name="changelog">Änderungen</string>
<!--Log Fragment-->
<string name="menuSaveToSd">Auf SD Karte speichern</string>
<string name="menuSend">Senden</string>
<string name="menuReload">erneut laden</string>
<string name="menuClearLog">Log jetzt löschen</string>
<string name="menuSaveToSd">Log auf SD-Karte speichern</string>
<string name="menuReload">Log erneut laden</string>
<string name="menuClearLog">Log löschen</string>
<string name="logs_cleared">Log erfolgreich gelöscht</string>
<string name="log_is_empty">Log ist leer</string>
<string name="logs_save_failed">Konnte Log nicht auf SD Karte speichern:</string>
<string name="logs_save_failed">Konnte den Log nicht auf der SD-Karte speichern:</string>
<!--About Activity-->
<string name="about">Über</string>
<string name="app_developers">Entwickler</string>
<string name="app_developers_"><![CDATA[Anwendung entwickelt von <a href="https://github.com/topjohnwu">topjohnwu</a> in Zusammenarbeit mit <a href="https://github.com/d8ahazard">Digitalhigh</a> und <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">Anwendungs changelog</string>
<string name="translators">skalnet</string>
<string name="app_version">Anwendungs Version</string>
<string name="app_developers">Hauptentwickler</string>
<string name="app_developers_"><![CDATA[App entwickelt von <a href="https://github.com/topjohnwu">topjohnwu</a> unter Mitwirkung von <a href="https://github.com/d8ahazard">Digitalhigh</a> und <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">Änderungen</string>
<string name="translators">skalnet, c727</string>
<string name="app_version">Version</string>
<string name="app_source_code">Quelltext</string>
<string name="donation">Spende</string>
<string name="app_translators">Anwendungs Übersetzer</string>
<string name="support_thread">Support thread</string>
<string name="app_translators">Übersetzer</string>
<string name="support_thread">Hilfeforum</string>
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">Diese Funktion wird ohne erlaubnis auf den externen Speicher zu schreiben nicht funktionieren.</string>
<string name="no_thanks">Nein Danke</string>
<string name="permissionNotGranted">Diese Funktion benötigt Rechte zum Schreiben auf den externen Speicher.</string>
<string name="no_thanks">Nein danke</string>
<string name="yes">Ja</string>
<string name="repo_install_title">Installiere %1$s</string>
<string name="repo_install_msg">Wollen Sie %1$s installieren?</string>
<string name="download_install">Herunterladen und Installieren</string>
<string name="download_file_error">Fehler beim Herunterladen</string>
<string name="install_error">Fehler beim Installieren!</string>
<string name="manual_install_1">Fehler beim Flashen. Datei in %1$s abgelegt\n Bitte in recovery manuell flashen</string>
<string name="invalid_zip">Diese Datei ist kein Magisk Modul!!</string>
<string name="repo_install_msg">Möchtest du %1$s installieren?</string>
<string name="download_install">Herunterladen &amp; installieren</string>
<string name="goto_install">Zu \"Installieren\" wechseln</string>
<string name="download_file_error">Fehler beim Herunterladen der Datei</string>
<string name="install_error">Fehler bei der Installation!</string>
<string name="manual_install_1">Zip-Datei unter %1$s gespeichert</string>
<string name="manual_install_2">Manuell mittels Recovery flashen</string>
<string name="invalid_zip">Die Zip-Datei ist kein Magisk-Modul!</string>
<string name="reboot_title">Installation erfolgreich!</string>
<string name="reboot_msg">Wollen Sie jetzt neu starten?</string>
<string name="reboot_msg">Möchtest du jetzt neustarten?</string>
<string name="reboot">Neustart</string>
<string name="copying_msg">Kopiere Zip ins temp-Verzeichnis</string>
<string name="zip_install_progress_title">Installiere</string>
<string name="zip_install_progress_msg">"Installiere %1$s …"</string>
<string name="no_magisk_title">Kein Magisk Installiert!</string>
<string name="no_magisk_msg">Wollen Sie Magisk herunterladen und installieren?</string>
<string name="zip_install_unzip_zip_msg">Entpacke Zip-Datei…</string>
<string name="zip_install_process_zip_msg">Verarbeite Zip-Datei…</string>
<string name="zip_install_progress_msg">"Installiere %1$s…"</string>
<string name="no_magisk_title">Magisk ist nicht installiert!</string>
<string name="no_magisk_msg">Möchtest du Magisk herunterladen und installieren?</string>
<string name="downloading_toast">Herunterladen von %1$s</string>
<string name="magisk_update_title">Neues Magisk-Update verfügbar!</string>
<string name="magisk_update_message">Magisk-Update v%1$.1f ist verfügbar, möchtest du es installieren?</string>
<string name="settings_reboot_toast">Neustarten, um die Änderungen anzuwenden</string>
<string name="check_release_notes">Änderungen anzeigen</string>
<string name="repo_cache_cleared">Repo-Cache gelöscht</string>
<string name="safetyNet_hide_notice">Diese App benutzt SafetyNet, welches standardmäßig von Magisk Hide gehandhabt</string>
<string name="start_magiskhide">Starte Magisk Hide…</string>
<!--URL Templates-->
<!--Settings Fragment -->
<!--Settings Activity -->
<string name="settings_general_category">Allgemein</string>
<string name="settings_theme_title">Aussehen</string>
<string name="settings_theme_summary">Wähle ein Theme aus</string>
<string name="theme_default">Standard</string>
<string name="theme_dark">Dunkel</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 Informtionen der Online-Repos. Erzwingt eine Aktualisierung</string>
<string name="settings_disable_title">Magisk deaktivieren</string>
<string name="settings_disable_summary">Deaktiviert alles außer den root-Zugang (MagiskSU)</string>
<string name="settings_magiskhide_summary">Versteckt Magisk vor diversen Entdeckungsmethoden</string>
<string name="settings_busybox_title">BusyBox aktivieren</string>
<string name="settings_busybox_summary">Magisks eingebautes BusyBox zum PATH hinzufügen</string>
<string name="settings_busybox_summary">Magisk\'s integriertes BusyBox nach xbin mounten</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_development_category">Entwicklung</string>
<string name="settings_developer_logging_title">Aktiviere erweitertes logging zum debuggen</string>
<string name="settings_developer_logging_summary">Aktivieren um ausführlichere logs zu erhalten.</string>
<string name="settings_shell_logging_title">Aktiviere debug logging für Shell Befehle</string>
<string name="settings_shell_logging_summary">Aktivieren um alle Shell Befehle und Ausgaben zu loggen.</string>
<string name="settings_magiskhide_title">Aktiviere Magisk Hide</string>
<string name="settings_magiskhide_summary">Neu starten um Einstellungen zu aktivieren</string>
<string name="settings_su_app_adb">Apps und ADB</string>
<string name="settings_su_app">Nur Apps</string>
<string name="settings_su_adb">Nur ADB</string>
<string name="settings_su_disable">Deaktiviert</string>
<string name="settings_su_request_10">10 Sekunden</string>
<string name="settings_su_request_20">20 Sekunden</string>
<string name="settings_su_request_30">30 Sekunden</string>
<string name="settings_su_request_60">60 Sekunden</string>
<string name="superuser_access">Superuser-Zugriff</string>
<string name="auto_response">Automatisch beantworten</string>
<string name="request_timeout">Zeitlimit für Anfrage</string>
<string name="superuser_notification">Superuser-Benachrichtigung</string>
<string name="request_timeout_summary">%1$s Sekunden</string>
<!-- Strings related to Settings -->
<string name="settings_development_category">Entwickler</string>
<string name="settings_developer_logging_title">Erweiterte Fehlerprotokolle</string>
<string name="settings_developer_logging_summary">Für ausführliches Logging aktivieren</string>
<string name="settings_shell_logging_title">Fehlerprotokolle für Shell-Befehle</string>
<string name="settings_shell_logging_summary">Logging aller Shell-Befehle sowie deren Ausgabe</string>
<!-- Example General settings -->
<!-- Example settings for Data & Sync -->
<!-- Example settings for Notifications -->
<!--Superuser-->
<string name="su_request_title">Superuser-Anfrage</string>
<string name="deny_with_str">Verweigern%1$s</string>
<string name="deny">Verweigern</string>
<string name="prompt">Nachfragen</string>
<string name="grant">Gewähren</string>
<string name="su_warning">Erlaubt den vollen Zugriff auf das Gerät.\nVerweigere, wenn du dir unsicher bist!</string>
<string name="forever">Dauerhaft</string>
<string name="once">Nur diesmal</string>
<string name="tenmin">10 Min.</string>
<string name="twentymin">20 Min.</string>
<string name="thirtymin">30 Min.</string>
<string name="sixtymin">60 Min.</string>
<string name="su_allow_toast">Superuser-Rechte für %1$s gewährt</string>
<string name="su_deny_toast">Superuser-Rechte für %1$s verweigert</string>
<string name="no_apps_found">Keine Apps gefunden</string>
<string name="su_snack_grant">Superuser-Rechte werden für %1$s gewährt</string>
<string name="su_snack_deny">Superuser-Rechte werden für %1$s verweigert</string>
<string name="su_snack_notif_on">Benachrichtigungen sind für %1$s aktiviert</string>
<string name="su_snack_notif_off">Benachrichtigungen sind für %1$s deaktiviert</string>
<string name="su_snack_log_on">Logging ist für %1$s aktiviert</string>
<string name="su_snack_log_off">Logging ist für %1$s deaktiviert</string>
<string name="su_snack_revoke">Die Rechte für %1$s wurden entzogen</string>
<string name="su_revoke_title">Entziehen?</string>
<string name="su_revoke_msg">Möchtest du die Rechte für %1$s entziehen?</string>
<string name="toast">Popup</string>
<string name="none">Keine</string>
<!--Superuser logs-->
<string name="pid">PID:\u0020</string>
<string name="target_uid">Ziel-UID:\u0020</string>
<string name="command">Befehl:\u0020</string>
</resources>

View File

@@ -39,7 +39,6 @@
<!--Log Fragment-->
<string name="menuSaveToSd">Salvar a SD</string>
<string name="menuSend">Enviar</string>
<string name="menuReload">Recargar</string>
<string name="menuClearLog">Vaciar Log ahora</string>
<string name="logs_cleared">Log vaciado correctamente</string>
@@ -80,12 +79,9 @@
<!--Settings Activity -->
<string name="settings_general_category">General</string>
<string name="settings_theme_title">Tema</string>
<string name="settings_theme_summary">Selecciona un tema</string>
<string name="theme_default">Por defecto</string>
<string name="theme_dark">Oscuro</string>
<string name="settings_dark_theme_title">Tema</string>
<string name="settings_dark_theme_summary">Selecciona un tema</string>
<string name="settings_magiskhide_title">Habilitar Magisk Hide</string>
<string name="settings_magiskhide_summary">Ocultar Magisk de varias detecciones</string>
<string name="settings_busybox_title">Habilitar BusyBox</string>
<string name="settings_busybox_summary">Montar el busybox interno de Magisk en xbin</string>

View File

@@ -4,20 +4,46 @@
<!--Welcome Activity-->
<string name="navigation_drawer_open">Apri drawer di navigazione</string>
<string name="navigation_drawer_close">Chiudi drawer di navigazione</string>
<string name="magiskhide">Magisk Hide</string>
<string name="modules">Moduli</string>
<string name="downloads">Downloads</string>
<string name="log">Log</string>
<string name="settings">Impostazioni</string>
<string name="superuser">Superuser</string>
<string name="status">Stato</string>
<string name="install">Installa</string>
<!--Magisk Fragment-->
<!--Status Fragment-->
<string name="magisk_version">Versione Magisk: v%1$s</string>
<string name="magisk_version_error">Hai installato Magisk?</string>
<string name="checking_for_updates">Controlla aggiornamenti…</string>
<string name="magisk_update_available">Magisk v%1$.1f update!</string>
<string name="cannot_check_updates">Impossibile controllare aggiornamenti</string>
<string name="up_to_date">L\'ultima versione di %1$s è installata</string>
<string name="root_error">Rootato ma senza permessi, non autorizzato?</string>
<string name="not_rooted">Non rootato</string>
<string name="proper_root">Rootato correttamente</string>
<string name="safetyNet_check_text">Tap per avviare controllo SafetyNet</string>
<string name="checking_safetyNet_status">Controllo SafetyNet</string>
<string name="safetyNet_connection_failed">Impossibile collegarsi alle API Google</string>
<string name="safetyNet_connection_suspended">Connessione alle API Google sospesa</string>
<string name="safetyNet_error">Impossibile controllare SafetyNet, no Internet?</string>
<string name="safetyNet_fail">Errore SafetyNet: il profilo CTS non corrisponde</string>
<string name="safetyNet_pass">SafetyNet corretto</string>
<string name="root_info_warning">Funzionalità molto limitata</string>
<!--Install Fragment-->
<string name="auto_detect">"(Auto) %1$s"</string>
<string name="boot_image_title">Boot Image</string>
<string name="detect_button">Identifica</string>
<string name="advanced_settings_title">Impostazioni Avanzate</string>
<string name="keep_force_encryption">Mantieni crittografia forzata</string>
<string name="keep_dm_verity">Mantieni dm-verity</string>
<string name="current_magisk_title">Versione Magisk installata: v%1$s</string>
<string name="install_magisk_title">Ultima versione Magisk: v%1$.1f</string>
<!--Root Fragment-->
<!--Module Fragment-->
<string name="no_info_provided">(Nessuna informazione)</string>
@@ -28,7 +54,8 @@
<string name="disable_file_created">Il modulo sarà disattivato al prossimo riavvio</string>
<string name="disable_file_removed">Il modulo sarà abilitato al prossimo riavvio</string>
<string name="author">Creato da: %1$s</string>
<string name="fab_flash_zip">Flash Modulo Zip</string>
<!--Repo Fragment-->
<string name="update_available">Aggiornamento disponibile</string>
<string name="installed">Installato</string>
@@ -37,7 +64,6 @@
<!--Log Fragment-->
<string name="menuSaveToSd">Salva nella SD</string>
<string name="menuSend">Invia</string>
<string name="menuReload">Ricarica</string>
<string name="menuClearLog">Cancella log</string>
<string name="logs_cleared">Log creato con successo</string>
@@ -78,13 +104,10 @@
<!--Settings Fragment -->
<string name="settings_general_category">Generali</string>
<string name="settings_theme_title">Tema</string>
<string name="settings_theme_summary">Scegli un tema</string>
<string name="theme_default">Default</string>
<string name="theme_dark">Scuro</string>
<string name="settings_dark_theme_title">Tema</string>
<string name="settings_dark_theme_summary">Scegli un tema</string>
<string name="settings_busybox_title">Abilita BusyBox</string>
<string name="settings_busybox_summary">Make Magisk\'s built-in BusyBox be visible in PATH</string>
<string name="settings_development_category">Development</string>
<string name="settings_developer_logging_title">Abilita Debug log avanzato</string>

View File

@@ -37,7 +37,6 @@
<!--Log Fragment-->
<string name="menuSaveToSd">Opslaan op SD-kaart</string>
<string name="menuSend">Verzenden</string>
<string name="menuReload">Herladen</string>
<string name="menuClearLog">Maak log leeg</string>
<string name="logs_cleared">Log succesvol geleegd</string>
@@ -78,20 +77,16 @@
<!--Settings Fragment -->
<string name="settings_general_category">Algemeen</string>
<string name="settings_theme_title">Thema</string>
<string name="settings_theme_summary">Selecteer een thema</string>
<string name="theme_default">Standaard</string>
<string name="theme_dark">Donker</string>
<string name="settings_dark_theme_title">Thema</string>
<string name="settings_dark_theme_summary">Selecteer een thema</string>
<string name="settings_busybox_title">Schakel BusyBox in</string>
<string name="settings_busybox_summary">Maakt Magisk\'s ingebouwde BusyBox zichtbaar in PATH</string>
<string name="settings_development_category">Development</string>
<string name="settings_developer_logging_title">Geavanceerde debug logging</string>
<string name="settings_developer_logging_summary">Schakel dit in voor uitgebreidere logging.</string>
<string name="settings_shell_logging_title">Shell command debug loggin</string>
<string name="settings_shell_logging_summary">Schakel dit in om alle shell commands en output te loggen</string>
<string name="settings_magiskhide_title">Magisk verbergen</string>
<string name="settings_magiskhide_summary">Reboot om de instellingen toe te passen</string>
<!-- Strings related to Settings -->

View File

@@ -0,0 +1,192 @@
<resources>
<!--Universal-->
<!--Welcome Activity-->
<string name="navigation_drawer_open">Otwórz szufladę nawigacji</string>
<string name="navigation_drawer_close">Zamknij szufladę nawigacji</string>
<string name="magiskhide">Magisk Hide</string>
<string name="modules">Moduły</string>
<string name="downloads">Pobieranie</string>
<string name="superuser">Superuser</string>
<string name="log">Log</string>
<string name="settings">Ustawienia</string>
<string name="status">Status</string>
<string name="install">Instalacja</string>
<!--Status Fragment-->
<string name="magisk_version">Zainstalowany Magisk v%1$s</string>
<string name="magisk_version_disable">Magisk v%1$s wyłaczony</string>
<string name="magisk_version_error">Magisk nie jest zainstalowany</string>
<string name="checking_for_updates">Sprawdzanie aktualizacji…</string>
<string name="magisk_update_available">Magisk v%1$.1f dostępny!</string>
<string name="cannot_check_updates">Nie można sprawdzić dostępność aktualizacji, brak internetu</string>
<string name="up_to_date">Zainstalowana najnowsza wersja %1$s</string>
<string name="root_error">Zrotowany ale bez dostępu do roota</string>
<string name="not_rooted">Nie zrootowany</string>
<string name="proper_root">Prawidłowo zrootowany</string>
<string name="safetyNet_check_text">Dotknij aby sprawdzić SafetyNet</string>
<string name="checking_safetyNet_status">Sprawdzanie statusu SafetyNet…</string>
<string name="safetyNet_connection_failed">Nie można połączyć się z Google API</string>
<string name="safetyNet_connection_suspended">Połączenie z Google API zostało zawieszone</string>
<string name="safetyNet_error">Nie można sprawdzić SafetyNet bez Internetu</string>
<string name="safetyNet_fail">SafetyNet Failed: CTS profile mismatch</string>
<string name="safetyNet_pass">SafetyNet Prawidłowy</string>
<string name="root_info_warning">Funkcjonalność znacznie ograniczona</string>
<!--Install Fragment-->
<string name="auto_detect">"(Auto) %1$s"</string>
<string name="boot_image_title">Lokalizacja Boot Image</string>
<string name="detect_button">Wykryj</string>
<string name="advanced_settings_title">Zaawansowane Ustawienia</string>
<string name="keep_force_encryption">Keep force encryption</string>
<string name="keep_dm_verity">Keep dm-verity</string>
<string name="current_magisk_title">Zainstalowana Wersja Magisk: v%1$s</string>
<string name="install_magisk_title">Ostatnia Wersja Magisk: v%1$.1f</string>
<!--Module Fragment-->
<string name="no_info_provided">(Nie umieszczono informacji)</string>
<string name="no_modules_found">Nie znaleziono modułów</string>
<string name="update_file_created">Moduł zostanie zaktualizowany przy następnym restarcie</string>
<string name="remove_file_created">Moduł zostanie usunięty przy następnym uruchomieniu</string>
<string name="remove_file_deleted">Moduł nie zostanie usunięty podczas następnego restartu</string>
<string name="disable_file_created">Moduł zostanie wyłączony przy następnym restarcie</string>
<string name="disable_file_removed">Moduł zostanie włączony przy następnym restarcie</string>
<string name="author">Autor modu: %1$s</string>
<string name="fab_flash_zip">Zflashuj Moduł Zip</string>
<!--Repo Fragment-->
<string name="update_available">Aktualizacja jest dostępna</string>
<string name="installed">Zainstalowany</string>
<string name="not_installed">Nie zainstalowany</string>
<string name="changelog">Zmiany</string>
<!--Log Fragment-->
<string name="menuSaveToSd">Zapisz na SD</string>
<string name="menuReload">Załaduj ponownie</string>
<string name="menuClearLog">Wyczyść Log</string>
<string name="logs_cleared">Log wyczyszczony</string>
<string name="log_is_empty">Log jest pusty</string>
<string name="logs_save_failed">Nie można zapisać logu na karcie SD:</string>
<!--About Activity-->
<string name="about">O Aplikacji</string>
<string name="app_developers">Developerzy</string>
<string name="app_developers_"><![CDATA[Autorzy aplikacji <a href="https://github.com/topjohnwu">topjohnwu</a> in collaboration with <a href="https://github.com/d8ahazard">Digitalhigh</a> and <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
<string name="app_changelog">Zmiany w Aplikacji</string>
<string name="translators" />
<string name="app_version">Wersja Aplikacji</string>
<string name="app_source_code">Source code</string>
<string name="donation">Dotacja</string>
<string name="app_translators">Tłumacze Aplikacji</string>
<string name="support_thread">Strona Wsparcia</string>
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">Ta funkcja nie będzie działać bez uprawnień do zapisu na pamięci zewnętrznej.</string>
<string name="no_thanks">Nie dziękuję</string>
<string name="yes">Tak</string>
<string name="repo_install_title">Zainstaluj %1$s</string>
<string name="repo_install_msg">Czy chcesz zainstalować %1$s ?</string>
<string name="download_install">Pobierz i zainstaluj</string>
<string name="goto_install">Idź do sekcji \"Instalacja\"</string>
<string name="download_file_error">Błąd pobierania pliku</string>
<string name="install_error">Błąd instalacji!</string>
<string name="manual_install_1">Plik zip umieszczony w %1$s</string>
<string name="manual_install_2">Zflashuj go ręcznie w recovery</string>
<string name="invalid_zip">Ten zip nie jest Modułem Magisk!!</string>
<string name="reboot_title">Instalacja zakończona powodzeniem!</string>
<string name="reboot_msg">Czy chcesz teraz ponownie uruchomić?</string>
<string name="reboot">Restart</string>
<string name="copying_msg">Kopiowanie zip do katalogu temp</string>
<string name="zip_install_progress_title">Instalacja</string>
<string name="zip_install_unzip_zip_msg">Rozpakowywanie pliku zip …</string>
<string name="zip_install_process_zip_msg">Przetwarzanie pliku zip …</string>
<string name="zip_install_progress_msg">"Instalowanie %1$s …"</string>
<string name="no_magisk_title">Brak zainstalowanego Magisk!</string>
<string name="no_magisk_msg">Chcesz pobrać i zainstalować Magisk?</string>
<string name="downloading_toast">Pobieranie %1$s</string>
<string name="magisk_update_title">Nowa Wersja Magisk Dostepna!</string>
<string name="magisk_update_message">Magisk v%1$.1f jest dostępny, chcesz zainstalować?</string>
<string name="settings_reboot_toast">Uruchom ponownie, aby zastosować ustawienia</string>
<string name="check_release_notes">Sprawdź informacje o wydaniu</string>
<string name="repo_cache_cleared">Cache repozytorium wyczyszczone</string>
<string name="safetyNet_hide_notice">Ta aplikacja wykorzystuje SafetyNet\nJuż jest domyślnie obsługiwana przez MagiskHide</string>
<string name="start_magiskhide">Uruchamianie MagiskHide …</string>
<!--URL Templates-->
<!--Settings Activity -->
<string name="settings_general_category">Ogólne</string>
<string name="settings_dark_theme_title">Ciemny Motyw</string>
<string name="settings_dark_theme_summary">Włącz ciemny motyw</string>
<string name="settings_clear_cache_title">Wyczyść Cache Repozytorium</string>
<string name="settings_clear_cache_summary">Wyczyść cache dla repozytorium, wymusza odświeżenie online przez aplikację</string>
<string name="settings_disable_title">Wyłącz Montowanie Magisk</string>
<string name="settings_disable_summary">Disable Magic Mount, co uniemożliwi wszystkim modułom działanie</string>
<string name="settings_magiskhide_summary">Włącz Hide Magisk dla wykrytych aplikacji</string>
<string name="settings_busybox_title">Włącz BusyBox</string>
<string name="settings_busybox_summary">Zmień montowanie Magisk z wbudowanego busybox do xbin</string>
<string name="settings_hosts_title">Włącz systemless hosts</string>
<string name="settings_hosts_summary">Wsparcie systemless dla aplikacji Adblock</string>
<string name="settings_su_app_adb">Aplikacje i ADB</string>
<string name="settings_su_app">Tylko aplikacje</string>
<string name="settings_su_adb">Tylko ADB</string>
<string name="settings_su_disable">Wyłaczone</string>
<string name="settings_su_request_10">10 sekund</string>
<string name="settings_su_request_20">20 sekund</string>
<string name="settings_su_request_30">30 ssekund</string>
<string name="settings_su_request_60">60 sekund</string>
<string name="superuser_access">Dostęp Superuser</string>
<string name="auto_response">Automatyczna Odpowiedź</string>
<string name="request_timeout">Czas Prośby</string>
<string name="superuser_notification">Powiadomienia Superusera</string>
<string name="request_timeout_summary">%1$s sekund</string>
<string name="settings_development_category">Dla Developerów</string>
<string name="settings_developer_logging_title">Włącz zaawansowane logowanie debugowania</string>
<string name="settings_developer_logging_summary">Zaznacz, aby umożliwić pełne rejestrowanie</string>
<string name="settings_shell_logging_title">Włącz rejestrowanie poleceń powłoki debugowania</string>
<string name="settings_shell_logging_summary">Włącz, aby rejestrować wszystkie polecenia powłoki i wyjścia</string>
<!--Superuser-->
<string name="su_request_title">Prośba dostępu Superusera</string>
<string name="deny_with_str">Odmów%1$s</string>
<string name="deny">Odmów</string>
<string name="prompt">Zapytaj</string>
<string name="grant">Przyznaj</string>
<string name="su_warning">Udziela pełnego dostępu do urządzenia.\nOdmów jeśli nie jesteś pewien!</string>
<string name="forever">Zawsze</string>
<string name="once">Raz</string>
<string name="tenmin">10 min</string>
<string name="twentymin">20 min</string>
<string name="thirtymin">30 min</string>
<string name="sixtymin">60 min</string>
<string name="su_allow_toast">%1$s ma przyznane uprawnienia Superusera</string>
<string name="su_deny_toast">%1$s ma odmówione uprawnienia Superusera</string>
<string name="no_apps_found">Nie znaleziono aplikacji</string>
<string name="su_snack_grant">Przyznano uprawnienia Superuser dla %1$s</string>
<string name="su_snack_deny">Odmówiono uprawnień Superuser dla %1$s</string>
<string name="su_snack_notif_on">Powiadomienia dla %1$s są włączone</string>
<string name="su_snack_notif_off">Powiadomienia dla %1$s są wyłączone</string>
<string name="su_snack_log_on">Logowanie dla %1$s jest włączone</string>
<string name="su_snack_log_off">Logowanie dla %1$s jest wyłączone</string>
<string name="su_snack_revoke">%1$s uprawnienia są odwołane</string>
<string name="su_revoke_title">Odwołać?</string>
<string name="su_revoke_msg">Potwierdzasz odwołanie uprawnień %1$s?</string>
<string name="toast">Toast</string>
<string name="none">Brak</string>
<!--Superuser logs-->
<string name="pid">PID:\u0020</string>
<string name="target_uid">Indentyfikator UID:\u0020</string>
<string name="command">Komenda:\u0020</string>
</resources>

View File

@@ -1,15 +1,20 @@
<resources>
<!--Welcome Activity-->
<string name="navigation_drawer_open">Abrir gaveta de notificação</string>
<string name="navigation_drawer_close">Fechar gaveta de notificação</string>
<string name="magiskhide">Magisk Hide</string>
<string name="modules">Módulos</string>
<string name="downloads">Baixar</string>
<string name="superuser">Superusuário</string>
<string name="log">Registro</string>
<string name="settings">Configurações</string>
<string name="status">Status</string>
<string name="install">Instalar</string>
<!--Status Fragment-->
<string name="magisk_version">Magisk v%1$s Instalado</string>
<string name="magisk_version_error">Magisk não instalado</string>
@@ -29,7 +34,7 @@
<string name="safetyNet_fail">SafetyNet Falhou: CTS profile mismatch</string>
<string name="safetyNet_pass">SafetyNet Passado</string>
<string name="root_info_warning">Funcionalidade muito limitada</string>
<!--Install Fragment-->
<string name="auto_detect">"(Auto) %1$s"</string>
<string name="boot_image_title">Local da Boot Image</string>
@@ -39,7 +44,8 @@
<string name="keep_dm_verity">Keep dm-verity</string>
<string name="current_magisk_title">Versão instalada do Magisk: v%1$s</string>
<string name="install_magisk_title">Última versão do Magisk: v%1$.1f</string>
<!--Module Fragment-->
<string name="no_info_provided">(Nenhuma informação fornecida)</string>
<string name="no_modules_found">Nenhum módulo encontrado</string>
@@ -59,7 +65,6 @@
<!--Log Fragment-->
<string name="menuSaveToSd">Salvar no SD</string>
<string name="menuSend">Enviar</string>
<string name="menuReload">Recarregar</string>
<string name="menuClearLog">Limpar registro agora</string>
<string name="logs_cleared">Registro limpado com sucesso</string>
@@ -81,6 +86,7 @@
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">Este recurso não funcionará sem permissão de escrita do armazenamento externo.</string>
<string name="no_thanks">Não, Obrigado</string>
<string name="yes">Sim</string>
<string name="repo_install_title">Instalar %1$s</string>
<string name="repo_install_msg">Você deseja instalar%1$s ?</string>
<string name="download_install">Baixar &amp; instalar</string>
@@ -103,28 +109,84 @@
<string name="downloading_toast">Baixando %1$s</string>
<string name="magisk_update_title">Nova atualização do Magisk disponível!</string>
<string name="magisk_update_message">Magisk v%1$.1f Atualização está pronta, você quer instalar?</string>
<string name="settings_reboot_toast">Reinicie para aplicar configurações</string>
<string name="check_release_notes">Verificar as notas da atualização</string>
<string name="repo_cache_cleared">Cache do Repo. limpado</string>
<string name="safetyNet_hide_notice">Este aplicativo usa SafetyNet\nJá manipulado pelo MagiskHide por padrão</string>
<string name="start_magiskhide">Iniciando MagiskHide …</string>
<!--Settings Fragment -->
<string name="settings_general_category">Geral</string>
<string name="settings_theme_title">Tema</string>
<string name="settings_theme_summary">Escolha um tema</string>
<string name="theme_default">Padrão</string>
<string name="theme_dark">Escuro</string>
<string name="settings_magiskhide_title">Ativar Magisk Hide</string>
<string name="settings_dark_theme_title">Tema escuro</string>
<string name="settings_dark_theme_summary">Ativa o tema escuro</string>
<string name="settings_clear_cache_title">Limpar Repo Cache</string>
<string name="settings_clear_cache_summary">Limpe as informações armazenadas em cache para repos. online, forçando o aplicativo a atualizar online</string>
<string name="settings_magiskhide_summary">Ocultar Magisk de várias detecções</string>
<string name="settings_busybox_title">Ativar BusyBox</string>
<string name="settings_busybox_summary">Monta a busybox interna do Magisk\'s para xbin</string>
<string name="settings_hosts_title">Ativar systemless hosts</string>
<string name="settings_hosts_summary">Suporte do systemless para Adblock apps</string>
<string name="settings_su_app_adb">Aplicativos e ADB</string>
<string name="settings_su_app">Somente Aplicativos</string>
<string name="settings_su_adb">Somente ADB</string>
<string name="settings_su_disable">Desativado</string>
<string name="settings_su_request_10">10 segundos</string>
<string name="settings_su_request_20">20 segundos</string>
<string name="settings_su_request_30">30 segundos</string>
<string name="settings_su_request_60">60 segundos</string>
<string name="superuser_access">Acesso de superusuário</string>
<string name="auto_response">Resposta Automática</string>
<string name="request_timeout">Tempo limite de solicitação</string>
<string name="superuser_notification">Notificação do superusuário</string>
<string name="request_timeout_summary">%1$s segundos</string>
<string name="settings_development_category">Desenvolvimento</string>
<string name="settings_developer_logging_title">Ativar registro mais detalhado</string>
<string name="settings_developer_logging_summary">Marque essa opção para habilitar um registro mais detalhado</string>
<string name="settings_shell_logging_title">Ativar registro da shell de comando</string>
<string name="settings_shell_logging_summary">Marque esta opção para habilitar o registro de todos os comandos shell e de saída</string>
<string name="settings_reboot_toast">Reinicie para aplicar configurações</string>
<!--Superuser-->
<string name="su_request_title">Solicitação de superusuário</string>
<string name="deny_with_str">Negar%1$s</string>
<string name="deny">Negar</string>
<string name="prompt">Painel</string>
<string name="grant">Permitir</string>
<string name="su_warning">Concede acesso total ao seu dispositivo.\nNegue se você não tiver certeza!</string>
<string name="forever">Sempre</string>
<string name="once">Uma vez</string>
<string name="tenmin">10 minutos</string>
<string name="twentymin">20 minutos</string>
<string name="thirtymin">30 minutos</string>
<string name="sixtymin">60 minutos</string>
<string name="su_allow_toast">%1$s foi permitido o acesso de superusuário</string>
<string name="su_deny_toast">%1$s foi negado o acesso de superusuário</string>
<string name="no_apps_found">Não foram encontrados apps</string>
<string name="su_snack_grant">Acesso de superusuário do %1$s está permitido</string>
<string name="su_snack_deny">Acesso de superusuário do %1$s está negado</string>
<string name="su_snack_notif_on">Notificações do %1$s está ativado</string>
<string name="su_snack_notif_off">Notificações do %1$s está desativado</string>
<string name="su_snack_log_on">Registro do %1$s está ativado</string>
<string name="su_snack_log_off">Registro do %1$s está desativado</string>
<string name="su_snack_revoke">%1$s Direitos foi revogados</string>
<string name="su_revoke_title">Revogar?</string>
<string name="su_revoke_msg">Revogar os diretos do %1$s, Confirmar?</string>
<string name="toast">Notificação simples</string>
<string name="none">Nenhum</string>
<!--Superuser logs-->
<string name="pid">PID:\u0020</string>
<string name="target_uid">Alvo UID:\u0020</string>
<string name="command">Comando:\u0020</string>
<string name="settings_disable_title">Desativar Magic Mount</string>
<string name="settings_disable_summary">Desativa Magic Mount, Isso impedirá que todos os módulos funcionem</string>
</resources>

View File

@@ -7,6 +7,7 @@
<string name="magiskhide">Magisk 隐藏</string>
<string name="modules">模块</string>
<string name="downloads">下载</string>
<string name="superuser">超级用户</string>
<string name="log">日志</string>
<string name="settings">设置</string>
<string name="status">状态</string>
@@ -14,7 +15,7 @@
<!--Status Fragment-->
<string name="magisk_version">已安装 Magisk v%1$s</string>
<string name="magisk_version_error">你是否已安装 Magisk</string>
<string name="magisk_version_error">安装 Magisk</string>
<string name="checking_for_updates">正在检查更新…</string>
<string name="magisk_update_available">Magisk 可更新到 v%1$.1f</string>
@@ -23,7 +24,10 @@
<string name="root_error">已 ROOT 但没有 ROOT 权限,未授予权限?</string>
<string name="not_rooted">未 ROOT</string>
<string name="proper_root">已正确 ROOT</string>
<string name="safetyNet_check_text">点击启动 SafetyNet 检查</string>
<string name="checking_safetyNet_status">正在检查 SafetyNet 状态…</string>
<string name="safetyNet_connection_failed">无法连接至 Google API</string>
<string name="safetyNet_connection_suspended">与 Google API 的连接已暂停</string>
<string name="safetyNet_error">无法检查 SafetyNet没有网络连接</string>
<string name="safetyNet_fail">SafetyNet 失败CTS 配置文件不匹配</string>
<string name="safetyNet_pass">SafetyNet 已通过</string>
@@ -36,7 +40,8 @@
<string name="advanced_settings_title">高级设置</string>
<string name="keep_force_encryption">保持强制加密</string>
<string name="keep_dm_verity">保持 dm-verity</string>
<string name="install_magisk_title">安装 Magisk 版本v%1$.1f</string>
<string name="current_magisk_title">安装 Magisk 版本v%1$s</string>
<string name="install_magisk_title">最新的 Magisk 版本v%1$.1f</string>
<!--Module Fragment-->
<string name="no_info_provided">(未提供信息)</string>
@@ -57,7 +62,6 @@
<!--Log Fragment-->
<string name="menuSaveToSd">保存到 SD 卡</string>
<string name="menuSend">发送</string>
<string name="menuReload">重载</string>
<string name="menuClearLog">清除日志</string>
<string name="logs_cleared">日志已成功清除</string>
@@ -79,6 +83,7 @@
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">未授予写入外置存储权限,此功能无法正常工作。</string>
<string name="no_thanks">不,谢谢</string>
<string name="yes"></string>
<string name="repo_install_title">安装 %1$s</string>
<string name="repo_install_msg">你想要安装 %1$s 吗?</string>
<string name="download_install">下载并安装</string>
@@ -99,22 +104,42 @@
<string name="no_magisk_title">未安装 Magisk</string>
<string name="no_magisk_msg">你想要下载并安装 Magisk 吗?</string>
<string name="downloading_toast">正在下载 %1$s</string>
<string name="magisk_update_title">Magisk 可更新!</string>
<string name="magisk_update_message">Magisk 已有新版本 v%1$.1f,你想要安装吗?</string>
<string name="settings_reboot_toast">重启以应用设置</string>
<string name="check_release_notes">查看发布说明</string>
<string name="repo_cache_cleared">资源库缓存已清除</string>
<string name="safetyNet_hide_notice">此应用使用了 SafetyNet\n已默认由 MagiskHide 处理</string>
<string name="start_magiskhide">正在启动 MagiskHide …</string>
<!--URL Templates-->
<!--Settings Activity -->
<string name="settings_general_category">常规</string>
<string name="settings_theme_title">主题</string>
<string name="settings_theme_summary">选择一个主题</string>
<string name="theme_default">默认</string>
<string name="theme_dark">深色</string>
<string name="settings_dark_theme_title">深色主题</string>
<string name="settings_dark_theme_summary">使用深色主题</string>
<string name="settings_clear_cache_title">清除资源库缓存</string>
<string name="settings_clear_cache_summary">清除已缓存的在线资源库信息,强制应用刷新在线数据</string>
<string name="settings_magiskhide_title">启用 Magisk 隐藏</string>
<string name="settings_magiskhide_summary">隐藏 Magisk 使其不被多种方法检测到</string>
<string name="settings_busybox_title">启用 BusyBox</string>
<string name="settings_busybox_summary">将 Magisk 内置的 Busybox 挂载到 xbin</string>
<string name="settings_hosts_title">启用 Systemless hosts</string>
<string name="settings_hosts_summary">Systemless 支持广告屏蔽应用</string>
<string name="settings_hosts_title">Systemless hosts</string>
<string name="settings_hosts_summary">为广告屏蔽应用提供 Systemless hosts 支持</string>
<string name="settings_su_app_adb">应用和 ADB</string>
<string name="settings_su_app">仅应用</string>
<string name="settings_su_adb">仅 ADB</string>
<string name="settings_su_disable">已禁用</string>
<string name="settings_su_request_10">10 秒</string>
<string name="settings_su_request_20">20 秒</string>
<string name="settings_su_request_30">30 秒</string>
<string name="settings_su_request_60">60 秒</string>
<string name="superuser_access">超级用户访问权限</string>
<string name="auto_response">自动响应</string>
<string name="request_timeout">请求超时</string>
<string name="superuser_notification">超级用户通知</string>
<string name="request_timeout_summary">%1$s 秒</string>
<string name="settings_development_category">开发</string>
<string name="settings_developer_logging_title">启用高级调试日志记录</string>
@@ -122,6 +147,39 @@
<string name="settings_shell_logging_title">启用 shell 命令调试日志记录</string>
<string name="settings_shell_logging_summary">勾选此项以启用对所有 shell 命令及输出的日志记录</string>
<string name="settings_reboot_toast">重启以应用设置</string>
<!--Superuser-->
<string name="su_request_title">超级用户请求</string>
<string name="deny_with_str">拒绝 %1$s</string>
<string name="deny">拒绝</string>
<string name="prompt">提示</string>
<string name="grant">允许</string>
<string name="su_warning">将授予对你设备的完全访问权限。\n如果你不确定请拒绝</string>
<string name="forever">永久</string>
<string name="once">一次</string>
<string name="tenmin">10 分钟</string>
<string name="twentymin">20 分钟</string>
<string name="thirtymin">30 分钟</string>
<string name="sixtymin">60 分钟</string>
<string name="su_allow_toast">%1$s 已被授予超级用户权限</string>
<string name="su_deny_toast">%1$s 已被拒绝超级用户权限</string>
<string name="no_apps_found">未找到应用</string>
<string name="su_snack_grant">已授予 %1$s 超级用户权限</string>
<string name="su_snack_deny">已拒绝 %1$s 超级用户权限</string>
<string name="su_snack_notif_on">%1$s 的通知已启用</string>
<string name="su_snack_notif_off">%1$s 的通知已禁用</string>
<string name="su_snack_log_on">对 %1$s 的日志记录已启用</string>
<string name="su_snack_log_off">对 %1$s 的日志记录已禁用</string>
<string name="su_snack_revoke">%1$s 的权限已撤销</string>
<string name="su_revoke_title">撤销</string>
<string name="su_revoke_msg">确认撤销 %1$s 的权限?</string>
<string name="toast">消息提示</string>
<string name="none"></string>
<!--Superuser logs-->
<string name="pid">PID:\u0020</string>
<string name="target_uid">目标 UID:\u0020</string>
<string name="command">命令:\u0020</string>
<string name="settings_disable_title">禁用魔法挂载(Magic Mount)</string>
<string name="settings_disable_summary">禁用魔法挂载,所有模块将不会生效</string>
</resources>

View File

@@ -1,11 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="themes">
<item>@string/theme_default</item>
<item>@string/theme_dark</item>
<string-array name="allow_timeout">
<item>@string/forever</item>
<item>@string/once</item>
<item>@string/tenmin</item>
<item>@string/twentymin</item>
<item>@string/thirtymin</item>
<item>@string/sixtymin</item>
</string-array>
<string-array name="themes_values">
<item>@string/theme_default_value</item>
<item>@string/theme_dark_value</item>
<string-array name="su_access">
<item>@string/settings_su_disable</item>
<item>@string/settings_su_app</item>
<item>@string/settings_su_adb</item>
<item>@string/settings_su_app_adb</item>
</string-array>
<string-array name="value_array">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
</string-array>
<string-array name="request_timeout">
<item>@string/settings_su_request_10</item>
<item>@string/settings_su_request_20</item>
<item>@string/settings_su_request_30</item>
<item>@string/settings_su_request_60</item>
</string-array>
<string-array name="request_timeout_value">
<item>10</item>
<item>20</item>
<item>30</item>
<item>60</item>
</string-array>
<string-array name="auto_response">
<item>@string/prompt</item>
<item>@string/deny</item>
<item>@string/grant</item>
</string-array>
<string-array name="su_notification">
<item>@string/none</item>
<item>@string/toast</item>
</string-array>
</resources>

View File

@@ -20,4 +20,6 @@
<color name="dh_icons">#dedede</color>
<color name="dh_divider">#313131</color>
<color name="su_request_background">#e0e0e0</color>
</resources>

View File

@@ -9,6 +9,7 @@
<string name="magiskhide">Magisk Hide</string>
<string name="modules">Modules</string>
<string name="downloads">Downloads</string>
<string name="superuser">Superuser</string>
<string name="log">Log</string>
<string name="settings">Settings</string>
<string name="status">Status</string>
@@ -16,6 +17,7 @@
<!--Status Fragment-->
<string name="magisk_version">Installed Magisk v%1$s</string>
<string name="magisk_version_disable">Magisk v%1$s disabled</string>
<string name="magisk_version_error">Magisk not installed</string>
<string name="checking_for_updates">Checking for updates…</string>
@@ -44,6 +46,8 @@
<string name="current_magisk_title">Installed Magisk Version: v%1$s</string>
<string name="install_magisk_title">Latest Magisk Version: v%1$.1f</string>
<string name="magiskify" translatable="false">Magiskify</string>
<string name="uninstall">Uninstall</string>
<string name="reboot_countdown">Rebooting in %1$d</string>
<!--Module Fragment-->
<string name="no_info_provided">(No info provided)</string>
@@ -64,7 +68,6 @@
<!--Log Fragment-->
<string name="menuSaveToSd">Save to SD</string>
<string name="menuSend">Send</string>
<string name="menuReload">Reload</string>
<string name="menuClearLog">Clear log now</string>
<string name="logs_cleared">Log successfully cleared</string>
@@ -86,6 +89,7 @@
<!--Toasts, Dialogs-->
<string name="permissionNotGranted">This feature will not work without permission to write external storage.</string>
<string name="no_thanks">No thanks</string>
<string name="yes">Yes</string>
<string name="repo_install_title">Install %1$s</string>
<string name="repo_install_msg">Do you want to install %1$s ?</string>
<string name="download_install">Download &amp; install</string>
@@ -111,6 +115,11 @@
<string name="settings_reboot_toast">Reboot to apply settings</string>
<string name="check_release_notes">Check release notes</string>
<string name="repo_cache_cleared">Repo cache cleared</string>
<string name="safetyNet_hide_notice">This app uses SafetyNet\nAlready handled by MagiskHide by default</string>
<string name="start_magiskhide">Starting MagiskHide …</string>
<string name="no_magisksu_title">Not using MagiskSU!</string>
<string name="no_magisksu_msg">You are not rooted with MagiskSU, using MagiskHide itself might not be enough!\nIt\'s not officially supported, and you would need additional tools (e.g suhide) to pass Safety Net.</string>
<string name="understand">I understand</string>
<!--URL Templates-->
<string name="url_main" translatable="false">https://api.github.com/orgs/Magisk-Modules-Repo/repos</string>
@@ -119,29 +128,70 @@
<!--Settings Activity -->
<string name="settings_general_category">General</string>
<string name="settings_theme_title">Theme</string>
<string name="settings_theme_summary">Select a theme</string>
<string name="theme_default">Default</string>
<string name="theme_dark">Dark</string>
<string name="settings_dark_theme_title">Dark Theme</string>
<string name="settings_dark_theme_summary">Enable dark theme</string>
<string name="settings_clear_cache_title">Clear Repo Cache</string>
<string name="settings_clear_cache_summary">Clear the cached information for online repos, forces the app to refresh online</string>
<string name="settings_magiskhide_title">Enable Magisk Hide</string>
<string name="settings_disable_title">Disable Magisk</string>
<string name="settings_disable_summary">Everything will be disabled except root (MagiskSU)</string>
<string name="settings_magiskhide_summary">Hide Magisk from various detections</string>
<string name="settings_busybox_title">Enable BusyBox</string>
<string name="settings_busybox_summary">Bind mount Magisk\'s built-in busybox to xbin</string>
<string name="settings_hosts_title">Enable systemless hosts</string>
<string name="settings_hosts_summary">Systemless support for Adblock apps</string>
<string name="settings_hosts_title">Systemless hosts</string>
<string name="settings_hosts_summary">Systemless hosts support for Adblock apps</string>
<string name="settings_development_category">Development</string>
<string name="settings_su_app_adb">Apps and ADB</string>
<string name="settings_su_app">Apps only</string>
<string name="settings_su_adb">ADB only</string>
<string name="settings_su_disable">Disabled</string>
<string name="settings_su_request_10">10 seconds</string>
<string name="settings_su_request_20">20 seconds</string>
<string name="settings_su_request_30">30 seconds</string>
<string name="settings_su_request_60">60 seconds</string>
<string name="superuser_access">Superuser Access</string>
<string name="auto_response">Automatic Response</string>
<string name="request_timeout">Request Timeout</string>
<string name="superuser_notification">Superuser Notification</string>
<string name="request_timeout_summary">%1$s seconds</string>
<string name="settings_development_category">App Development</string>
<string name="settings_developer_logging_title">Enable advanced debug logging</string>
<string name="settings_developer_logging_summary">Check this to enable verbose logging</string>
<string name="settings_shell_logging_title">Enable shell command debug logging</string>
<string name="settings_shell_logging_summary">Check this to enable logging all shell commands and its output</string>
<!-- Themes -->
<string name="theme_default_value" translatable="false">default</string>
<string name="theme_dark_value" translatable="false">dark</string>
<!--Superuser-->
<string name="su_request_title">Superuser Request</string>
<string name="deny_with_str">Deny%1$s</string>
<string name="deny">Deny</string>
<string name="prompt">Prompt</string>
<string name="grant">Grant</string>
<string name="su_warning">Grants full access to your device.\nDeny if you\'re not sure!</string>
<string name="forever">Forever</string>
<string name="once">Once</string>
<string name="tenmin">10 min</string>
<string name="twentymin">20 min</string>
<string name="thirtymin">30 min</string>
<string name="sixtymin">60 min</string>
<string name="su_allow_toast">%1$s is granted Superuser rights</string>
<string name="su_deny_toast">%1$s is denied Superuser rights</string>
<string name="no_apps_found">No apps found</string>
<string name="su_snack_grant">Superuser rights of %1$s is granted</string>
<string name="su_snack_deny">Superuser rights of %1$s is denied</string>
<string name="su_snack_notif_on">Notifications of %1$s is enabled</string>
<string name="su_snack_notif_off">Notifications of %1$s is disabled</string>
<string name="su_snack_log_on">Logging of %1$s is enabled</string>
<string name="su_snack_log_off">Logging of %1$s is disabled</string>
<string name="su_snack_revoke">%1$s rights are revoked</string>
<string name="su_revoke_title">Revoke?</string>
<string name="su_revoke_msg">Confirm to revoke %1$s rights?</string>
<string name="toast">Toast</string>
<string name="none">None</string>
<!--Superuser logs-->
<string name="pid">PID:\u0020</string>
<string name="target_uid">Target UID:\u0020</string>
<string name="command">Command:\u0020</string>
</resources>

View File

@@ -121,4 +121,9 @@
<item name="android:windowTranslucentNavigation">true</item>
</style>
<style name="SuRequest" parent="Theme.AppCompat.Light.Dialog">
<item name="android:windowCloseOnTouchOutside">false</item>
<item name="android:colorBackground">@color/su_request_background</item>
</style>
</resources>

View File

@@ -4,13 +4,11 @@
<PreferenceCategory
android:title="@string/settings_general_category">
<ListPreference
android:key="theme"
android:title="@string/settings_theme_title"
android:summary="@string/settings_theme_summary"
android:defaultValue="@string/theme_default_value"
android:entries="@array/themes"
android:entryValues="@array/themes_values"/>
<SwitchPreference
android:defaultValue="false"
android:title="@string/settings_dark_theme_title"
android:summary="@string/settings_dark_theme_summary"
android:key="dark_theme" />
<Preference
android:key="clear"
@@ -20,21 +18,27 @@
</PreferenceCategory>
<PreferenceCategory
android:title="Magisk">
android:key="magisk"
android:title="@string/magisk">
<CheckBoxPreference
android:key="magiskhide"
android:defaultValue="false"
android:title="@string/settings_magiskhide_title"
android:summary="@string/settings_magiskhide_summary" />
android:key="disable"
android:title="@string/settings_disable_title"
android:summary="@string/settings_disable_summary" />
<CheckBoxPreference
<SwitchPreference
android:key="busybox"
android:defaultValue="false"
android:title="@string/settings_busybox_title"
android:summary="@string/settings_busybox_summary" />
<CheckBoxPreference
<SwitchPreference
android:key="magiskhide"
android:defaultValue="false"
android:title="@string/magiskhide"
android:summary="@string/settings_magiskhide_summary" />
<SwitchPreference
android:key="hosts"
android:defaultValue="false"
android:title="@string/settings_hosts_title"
@@ -42,16 +46,46 @@
</PreferenceCategory>
<PreferenceCategory
android:key="superuser"
android:title="@string/superuser">
<ListPreference
android:key="su_access"
android:title="@string/superuser_access"
android:entries="@array/su_access"
android:entryValues="@array/value_array" />
<ListPreference
android:key="su_auto_response"
android:title="@string/auto_response"
android:entries="@array/auto_response"
android:entryValues="@array/value_array" />
<ListPreference
android:key="su_request_timeout"
android:title="@string/request_timeout"
android:entries="@array/request_timeout"
android:entryValues="@array/request_timeout_value" />
<ListPreference
android:key="su_notification"
android:title="@string/superuser_notification"
android:entries="@array/su_notification"
android:entryValues="@array/value_array" />
</PreferenceCategory>
<PreferenceCategory
android:title="@string/settings_development_category">
<CheckBoxPreference
<SwitchPreference
android:key="developer_logging"
android:defaultValue="false"
android:title="@string/settings_developer_logging_title"
android:summary="@string/settings_developer_logging_summary" />
<CheckBoxPreference
<SwitchPreference
android:key="shell_logging"
android:defaultValue="false"
android:title="@string/settings_shell_logging_title"

View File

@@ -6,7 +6,7 @@ buildscript {
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.2.3'
classpath 'com.android.tools.build:gradle:2.3.0-beta3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@@ -15,4 +15,8 @@ org.gradle.jvmargs=-Xmx4096m -XX:MaxPermSize=4096m -XX:+HeapDumpOnOutOfMemoryErr
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
org.gradle.parallel=true
org.gradle.parallel=true
# When set to true the Gradle daemon is used to run the build. For local developer builds this is our favorite property.
# The developer environment is optimized for speed and feedback so we nearly always run Gradle jobs with the daemon.
org.gradle.daemon=true

View File

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip