Compare commits
80 Commits
manager-v3
...
manager-v4
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d1b5ebad7d | ||
![]() |
f4ce813de9 | ||
![]() |
b44ac994d8 | ||
![]() |
333948814c | ||
![]() |
1a51ad6e01 | ||
![]() |
22a5c11f0d | ||
![]() |
51b22d1ad4 | ||
![]() |
bef5969580 | ||
![]() |
c6bf7bb9cd | ||
![]() |
2a84d92cbf | ||
![]() |
62de36b0da | ||
![]() |
03a9aaeff7 | ||
![]() |
45765e292d | ||
![]() |
6e28a26015 | ||
![]() |
9150bf720d | ||
![]() |
845864679c | ||
![]() |
b3b2149ebb | ||
![]() |
0886dca385 | ||
![]() |
53198ba4a7 | ||
![]() |
a9652ee1fd | ||
![]() |
75caf2f01c | ||
![]() |
65bab2666e | ||
![]() |
6d93ae399a | ||
![]() |
7239c2e31a | ||
![]() |
43b7ef8110 | ||
![]() |
99ef0b8cb4 | ||
![]() |
0efb4da0ee | ||
![]() |
ed7920d61e | ||
![]() |
c0379c8e25 | ||
![]() |
00a0e64fdd | ||
![]() |
0dc60debea | ||
![]() |
c44ae5888c | ||
![]() |
b9495cd1bb | ||
![]() |
bfec381933 | ||
![]() |
2dddb8df69 | ||
![]() |
d30397e9c0 | ||
![]() |
d9597549fd | ||
![]() |
13512b4146 | ||
![]() |
49e546919a | ||
![]() |
586015c2ed | ||
![]() |
4a7e067d1a | ||
![]() |
9bc0b7f183 | ||
![]() |
cd4dfc9861 | ||
![]() |
09bdbc1224 | ||
![]() |
978b3a64c5 | ||
![]() |
651547ef20 | ||
![]() |
b4d95977d0 | ||
![]() |
5d8bb897db | ||
![]() |
84c8ecb372 | ||
![]() |
61abe5b948 | ||
![]() |
a5b573eaaa | ||
![]() |
cbb32f82eb | ||
![]() |
ca9334b2df | ||
![]() |
959ed7f866 | ||
![]() |
a5c0411be0 | ||
![]() |
32e1303742 | ||
![]() |
7263b6fe89 | ||
![]() |
46a4070f84 | ||
![]() |
c3c155a1ed | ||
![]() |
b067105660 | ||
![]() |
15ca18848e | ||
![]() |
67c9e2ead6 | ||
![]() |
3681177be4 | ||
![]() |
6eb814ef0b | ||
![]() |
bcc695234c | ||
![]() |
ad16a6fc1b | ||
![]() |
478b7eeb65 | ||
![]() |
151a153dc9 | ||
![]() |
ad131854ca | ||
![]() |
0bd0eb9e59 | ||
![]() |
cf16fd0104 | ||
![]() |
21b00ac6ca | ||
![]() |
57e6f3080c | ||
![]() |
89744100ce | ||
![]() |
a718f9bbfd | ||
![]() |
e81bc4f044 | ||
![]() |
4dbacd79ae | ||
![]() |
ae74d54451 | ||
![]() |
dc316c5669 | ||
![]() |
e9f04256c9 |
@@ -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})
|
@@ -8,10 +8,11 @@ android {
|
||||
applicationId "com.topjohnwu.magisk"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 12
|
||||
versionName "3.1"
|
||||
versionCode 22
|
||||
versionName "4.1"
|
||||
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'
|
||||
}
|
||||
|
2
app/proguard-rules.pro
vendored
@@ -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)
|
||||
|
@@ -8,11 +8,9 @@
|
||||
<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:name=".MagiskManager"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
@@ -41,6 +39,28 @@
|
||||
<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>
|
||||
|
||||
<service android:name=".receivers.BootReceiver$BootupIntentService" />
|
||||
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
|
150
app/src/main/assets/magisk_uninstaller.sh
Normal 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
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 73 KiB |
@@ -1,13 +1,12 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
@@ -17,6 +16,7 @@ import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.components.Activity;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
@@ -26,11 +26,12 @@ import java.io.InputStream;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class AboutActivity extends AppCompatActivity {
|
||||
public class AboutActivity extends Activity {
|
||||
|
||||
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 (getTopApplication().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) {
|
||||
|
@@ -1,9 +1,10 @@
|
||||
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.v7.widget.CardView;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -15,75 +16,130 @@ import android.widget.CheckBox;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.receivers.MagiskDlReceiver;
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
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 class InstallFragment extends Fragment implements CallbackEvent.Listener<Void> {
|
||||
|
||||
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, getApplication().magiskVersionString));
|
||||
installTitle.setText(getString(R.string.install_magisk_title, getApplication().remoteMagiskVersion));
|
||||
flashButton.setOnClickListener(v1 -> {
|
||||
String bootImage = bootBlock;
|
||||
if (bootImage == null) {
|
||||
bootImage = blockList.get(spinner.getSelectedItemPosition() - 1);
|
||||
String bootImage;
|
||||
if (getApplication().bootBlock != null) {
|
||||
if (spinner.getSelectedItemPosition() > 0)
|
||||
bootImage = getApplication().blockList.get(spinner.getSelectedItemPosition() - 1);
|
||||
else
|
||||
bootImage = getApplication().bootBlock;
|
||||
} else {
|
||||
bootImage = getApplication().blockList.get(spinner.getSelectedItemPosition());
|
||||
}
|
||||
String filename = "Magisk-v" + StatusFragment.remoteMagiskVersion + ".zip";
|
||||
String finalBootImage = bootImage;
|
||||
String filename = "Magisk-v" + getApplication().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()),
|
||||
getApplication().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(getApplication().releaseNoteLink)));
|
||||
})
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.show();
|
||||
});
|
||||
if (blockDetectionDone.isTriggered) {
|
||||
if (getApplication().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 (getApplication().blockDetectionDone.isTriggered) {
|
||||
updateUI();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackHandler.Event event) {
|
||||
public void onTrigger(CallbackEvent<Void> event) {
|
||||
updateUI();
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
List<String> items = new ArrayList<>(blockList);
|
||||
items.add(0, getString(R.string.auto_detect, bootBlock));
|
||||
List<String> items = new ArrayList<>(getApplication().blockList);
|
||||
if (getApplication().bootBlock != null)
|
||||
items.add(0, getString(R.string.auto_detect, getApplication().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 (getApplication().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);
|
||||
getApplication().blockDetectionDone.register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
CallbackHandler.unRegister(blockDetectionDone, this);
|
||||
public void onStop() {
|
||||
getApplication().blockDetectionDone.unRegister(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
unbinder.unbind();
|
||||
}
|
||||
}
|
||||
|
@@ -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.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 com.topjohnwu.magisk.components.Fragment;
|
||||
|
||||
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 (getApplication().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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
@@ -16,46 +15,42 @@ import android.view.ViewGroup;
|
||||
import android.widget.SearchView;
|
||||
|
||||
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
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 {
|
||||
public class MagiskHideFragment extends Fragment implements CallbackEvent.Listener<Void> {
|
||||
|
||||
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();
|
||||
|
||||
mSwipeRefreshLayout.setRefreshing(true);
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> new Async.LoadApps(packageManager).exec());
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> new Async.LoadApps(getApplication()).exec());
|
||||
|
||||
appAdapter = new ApplicationAdapter(packageManager);
|
||||
recyclerView.setAdapter(appAdapter);
|
||||
@@ -76,6 +71,9 @@ public class MagiskHideFragment extends Fragment implements CallbackHandler.Even
|
||||
}
|
||||
};
|
||||
|
||||
if (getApplication().packageLoadDone.isTriggered)
|
||||
onTrigger(getApplication().packageLoadDone);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -87,27 +85,28 @@ 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);
|
||||
}
|
||||
getApplication().packageLoadDone.register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
CallbackHandler.unRegister(packageLoadDone, this);
|
||||
public void onStop() {
|
||||
getApplication().packageLoadDone.unRegister(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackHandler.Event event) {
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
unbinder.unbind();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackEvent<Void> event) {
|
||||
Logger.dev("MagiskHideFragment: UI refresh");
|
||||
Async.LoadApps.Result result = (Async.LoadApps.Result) event.getResult();
|
||||
appAdapter.setLists(result.listApps, result.hideList);
|
||||
appAdapter.setLists(getApplication().appList, getApplication().magiskHideList);
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
if (!TextUtils.isEmpty(lastFilter)) {
|
||||
appAdapter.filter(lastFilter);
|
||||
|
237
app/src/main/java/com/topjohnwu/magisk/MagiskLogFragment.java
Normal 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.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.components.Fragment;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
153
app/src/main/java/com/topjohnwu/magisk/MagiskManager.java
Normal file
@@ -0,0 +1,153 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.SparseArray;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
import com.topjohnwu.magisk.module.Repo;
|
||||
import com.topjohnwu.magisk.superuser.Policy;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
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 MagiskManager extends Application {
|
||||
|
||||
public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk";
|
||||
public static final String MAGISK_MANAGER_BOOT = "/dev/.magisk_manager_boot";
|
||||
|
||||
// Events
|
||||
public final CallbackEvent<Void> blockDetectionDone = new CallbackEvent<>();
|
||||
public final CallbackEvent<Void> packageLoadDone = new CallbackEvent<>();
|
||||
public final CallbackEvent<Void> reloadMainActivity = new CallbackEvent<>();
|
||||
public final CallbackEvent<Void> moduleLoadDone = new CallbackEvent<>();
|
||||
public final CallbackEvent<Void> repoLoadDone = new CallbackEvent<>();
|
||||
public final CallbackEvent<Void> updateCheckDone = new CallbackEvent<>();
|
||||
public final CallbackEvent<Void> safetyNetDone = new CallbackEvent<>();
|
||||
public SparseArray<CallbackEvent<Policy>> uidMap = new SparseArray<>();
|
||||
|
||||
// Info
|
||||
public double magiskVersion;
|
||||
public String magiskVersionString = "(none)";
|
||||
public double remoteMagiskVersion = -1;
|
||||
public String magiskLink;
|
||||
public String releaseNoteLink;
|
||||
public int SNCheckResult = -1;
|
||||
public String bootBlock = null;
|
||||
public boolean isSuClient = false;
|
||||
public String suVersion = null;
|
||||
public boolean disabled = false;
|
||||
|
||||
// Data
|
||||
public ValueSortedMap<String, Repo> repoMap;
|
||||
public ValueSortedMap<String, Module> moduleMap;
|
||||
public List<String> blockList;
|
||||
public List<ApplicationInfo> appList;
|
||||
public List<String> magiskHideList;
|
||||
|
||||
// Configurations
|
||||
public static boolean shellLogging;
|
||||
public static boolean devLogging;
|
||||
|
||||
public boolean magiskHide;
|
||||
public boolean isDarkTheme;
|
||||
public int suRequestTimeout;
|
||||
public int suLogTimeout = 14;
|
||||
public int suAccessState;
|
||||
public int suResponseType;
|
||||
public int suNotificationType;
|
||||
|
||||
public SharedPreferences prefs;
|
||||
|
||||
private static Handler mHandler = new Handler();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
}
|
||||
|
||||
public void toast(String msg, int duration) {
|
||||
mHandler.post(() -> Toast.makeText(this, msg, duration).show());
|
||||
}
|
||||
|
||||
public void toast(int resId, int duration) {
|
||||
mHandler.post(() -> Toast.makeText(this, resId, duration).show());
|
||||
}
|
||||
|
||||
public void init() {
|
||||
isDarkTheme = prefs.getBoolean("dark_theme", false);
|
||||
devLogging = prefs.getBoolean("developer_logging", false);
|
||||
shellLogging = prefs.getBoolean("shell_logging", false);
|
||||
magiskHide = prefs.getBoolean("magiskhide", false);
|
||||
updateMagiskInfo();
|
||||
initSuAccess();
|
||||
initSuConfigs();
|
||||
// Initialize prefs
|
||||
prefs.edit()
|
||||
.putBoolean("dark_theme", isDarkTheme)
|
||||
.putBoolean("magiskhide", 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(suRequestTimeout))
|
||||
.putString("su_auto_response", String.valueOf(suResponseType))
|
||||
.putString("su_notification", String.valueOf(suNotificationType))
|
||||
.putString("su_access", String.valueOf(suAccessState))
|
||||
.apply();
|
||||
}
|
||||
|
||||
public void initSuConfigs() {
|
||||
suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10);
|
||||
suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0);
|
||||
suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1);
|
||||
}
|
||||
|
||||
public void initSuAccess() {
|
||||
List<String> ret = Shell.sh("su -v");
|
||||
if (Utils.isValidShellResponse(ret)) {
|
||||
suVersion = ret.get(0);
|
||||
isSuClient = suVersion.toUpperCase().contains("MAGISK");
|
||||
}
|
||||
if (isSuClient) {
|
||||
ret = Shell.sh("getprop persist.sys.root_access");
|
||||
if (Utils.isValidShellResponse(ret))
|
||||
suAccessState = Integer.parseInt(ret.get(0));
|
||||
else {
|
||||
Shell.su(true, "setprop persist.sys.root_access 3");
|
||||
suAccessState = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMagiskInfo() {
|
||||
List<String> ret = Shell.sh("getprop magisk.version");
|
||||
if (!Utils.isValidShellResponse(ret)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
ret = Shell.sh("getprop ro.magisk.disable");
|
||||
try {
|
||||
disabled = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
|
||||
} catch (NumberFormatException e) {
|
||||
disabled = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -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,32 +8,27 @@ 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;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.components.Activity;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import butterknife.BindView;
|
||||
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();
|
||||
public class MainActivity extends Activity
|
||||
implements NavigationView.OnNavigationItemSelectedListener, CallbackEvent.Listener<Void> {
|
||||
|
||||
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 (getTopApplication().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);
|
||||
getTopApplication().reloadMainActivity.register(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
CallbackHandler.register(StatusFragment.updateCheckDone, this);
|
||||
CallbackHandler.register(recreate, this);
|
||||
if (StatusFragment.updateCheckDone.isTriggered) {
|
||||
onTrigger(StatusFragment.updateCheckDone);
|
||||
}
|
||||
getTopApplication().updateCheckDone.register(this);
|
||||
// if (getTopApplication().updateCheckDone.isTriggered)
|
||||
// onTrigger(getTopApplication().updateCheckDone);
|
||||
checkHideSection();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
getTopApplication().updateCheckDone.unRegister(this);
|
||||
super.onPause();
|
||||
CallbackHandler.unRegister(StatusFragment.updateCheckDone, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
getTopApplication().reloadMainActivity.unRegister(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) {
|
||||
public void onTrigger(CallbackEvent<Void> event) {
|
||||
if (event == getTopApplication().updateCheckDone) {
|
||||
Menu menu = navigationView.getMenu();
|
||||
menu.findItem(R.id.install).setVisible(StatusFragment.remoteMagiskVersion > 0 &&
|
||||
Shell.rootAccess());
|
||||
} else if (event == recreate) {
|
||||
menu.findItem(R.id.install).setVisible(
|
||||
getTopApplication().remoteMagiskVersion > 0 && Shell.rootAccess());
|
||||
} else if (event == getTopApplication().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(
|
||||
getTopApplication().magiskVersion >= 8 && prefs.getBoolean("magiskhide", false));
|
||||
menu.findItem(R.id.modules).setVisible(getTopApplication().magiskVersion >= 4);
|
||||
menu.findItem(R.id.downloads).setVisible(getTopApplication().magiskVersion >= 4);
|
||||
menu.findItem(R.id.log).setVisible(true);
|
||||
menu.findItem(R.id.superuser).setVisible(getTopApplication().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);
|
||||
}
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Fragment;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
@@ -15,27 +14,27 @@ import android.widget.TextView;
|
||||
|
||||
import com.github.clans.fab.FloatingActionButton;
|
||||
import com.topjohnwu.magisk.adapters.ModulesAdapter;
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
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();
|
||||
public class ModulesFragment extends Fragment implements CallbackEvent.Listener<Void> {
|
||||
|
||||
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 +42,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);
|
||||
@@ -54,7 +53,7 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
|
||||
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> {
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
new Async.LoadModules().exec();
|
||||
new Async.LoadModules(getApplication()).exec();
|
||||
});
|
||||
|
||||
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@@ -69,7 +68,7 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
|
||||
}
|
||||
});
|
||||
|
||||
if (moduleLoadDone.isTriggered) {
|
||||
if (getApplication().moduleLoadDone.isTriggered) {
|
||||
updateUI();
|
||||
}
|
||||
|
||||
@@ -77,7 +76,7 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackHandler.Event event) {
|
||||
public void onTrigger(CallbackEvent<Void> event) {
|
||||
Logger.dev("ModulesFragment: UI refresh triggered");
|
||||
updateUI();
|
||||
}
|
||||
@@ -93,25 +92,32 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
CallbackHandler.register(moduleLoadDone, this);
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
getApplication().moduleLoadDone.register(this);
|
||||
getActivity().setTitle(R.string.modules);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
CallbackHandler.unRegister(moduleLoadDone, this);
|
||||
public void onStop() {
|
||||
getApplication().moduleLoadDone.unRegister(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
unbinder.unbind();
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
ModuleHelper.getModuleList(listModules);
|
||||
listModules.clear();
|
||||
listModules.addAll(getApplication().moduleMap.values());
|
||||
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));
|
||||
}
|
||||
|
@@ -1,6 +1,5 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.Fragment;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
@@ -16,24 +15,25 @@ import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.adapters.ReposAdapter;
|
||||
import com.topjohnwu.magisk.adapters.SimpleSectionedRecyclerViewAdapter;
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
import com.topjohnwu.magisk.module.Repo;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
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();
|
||||
public class ReposFragment extends Fragment implements CallbackEvent.Listener<Void> {
|
||||
|
||||
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);
|
||||
@@ -65,10 +68,10 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList
|
||||
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> {
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
new Async.LoadRepos(getActivity()).exec();
|
||||
new Async.LoadRepos(getApplication()).exec();
|
||||
});
|
||||
|
||||
if (repoLoadDone.isTriggered) {
|
||||
if (getApplication().repoLoadDone.isTriggered) {
|
||||
reloadRepos();
|
||||
updateUI();
|
||||
}
|
||||
@@ -90,7 +93,7 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackHandler.Event event) {
|
||||
public void onTrigger(CallbackEvent<Void> event) {
|
||||
Logger.dev("ReposFragment: UI refresh triggered");
|
||||
reloadRepos();
|
||||
updateUI();
|
||||
@@ -104,21 +107,39 @@ 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();
|
||||
getApplication().repoLoadDone.register(this);
|
||||
getActivity().setTitle(R.string.downloads);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
CallbackHandler.unRegister(repoLoadDone, this);
|
||||
public void onStop() {
|
||||
getApplication().repoLoadDone.unRegister(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
unbinder.unbind();
|
||||
}
|
||||
|
||||
private void reloadRepos() {
|
||||
ModuleHelper.getRepoLists(mUpdateRepos, mInstalledRepos, mOthersRepos);
|
||||
mUpdateRepos.clear();
|
||||
mInstalledRepos.clear();
|
||||
mOthersRepos.clear();
|
||||
for (Repo repo : getApplication().repoMap.values()) {
|
||||
Module module = getApplication().moduleMap.get(repo.getId());
|
||||
if (module != null) {
|
||||
if (repo.getVersionCode() > module.getVersionCode())
|
||||
mUpdateRepos.add(repo);
|
||||
else
|
||||
mInstalledRepos.add(repo);
|
||||
} else {
|
||||
mOthersRepos.add(repo);
|
||||
}
|
||||
}
|
||||
fUpdateRepos.clear();
|
||||
fInstalledRepos.clear();
|
||||
fOthersRepos.clear();
|
||||
@@ -129,7 +150,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 +165,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);
|
||||
|
@@ -1,38 +1,36 @@
|
||||
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.components.Activity;
|
||||
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;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
public class SettingsActivity extends Activity {
|
||||
|
||||
@BindView(R.id.toolbar) Toolbar toolbar;
|
||||
|
||||
@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 (getTopApplication().isDarkTheme) {
|
||||
setTheme(R.style.AppTheme_dh);
|
||||
}
|
||||
|
||||
@@ -74,52 +72,54 @@ 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;
|
||||
|
||||
private MagiskManager getApplication() {
|
||||
return (MagiskManager) getActivity().getApplication();
|
||||
}
|
||||
|
||||
@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(getApplication());
|
||||
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 (!getApplication().isSuClient)
|
||||
prefScreen.removePreference(suCategory);
|
||||
if (getApplication().magiskVersion < 11)
|
||||
prefScreen.removePreference(magiskCategory);
|
||||
if (getApplication().disabled) {
|
||||
busybox.setEnabled(false);
|
||||
magiskHide.setEnabled(false);
|
||||
hosts.setEnabled(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,62 +130,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 (getApplication().isDarkTheme != enabled) {
|
||||
getApplication().isDarkTheme = enabled;
|
||||
getActivity().recreate();
|
||||
MainActivity.recreate.trigger();
|
||||
getApplication().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(MagiskManager.MAGISK_DISABLE_FILE);
|
||||
} else {
|
||||
Utils.removeItem("/magisk/.core/magiskhide/enable");
|
||||
Utils.removeItem(MagiskManager.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 (!getApplication().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 +215,38 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
}
|
||||
}.exec();
|
||||
break;
|
||||
case "su_access":
|
||||
getApplication().suAccessState = Utils.getPrefsInt(prefs, "su_access", 0);
|
||||
Shell.su("setprop persist.sys.root_access " + getApplication().suAccessState);
|
||||
break;
|
||||
case "su_request_timeout":
|
||||
getApplication().suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10);
|
||||
break;
|
||||
case "su_auto_response":
|
||||
getApplication().suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0);
|
||||
break;
|
||||
case "su_notification":
|
||||
getApplication().suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1);
|
||||
break;
|
||||
case "developer_logging":
|
||||
Logger.devLog = prefs.getBoolean("developer_logging", false);
|
||||
getApplication().devLogging = prefs.getBoolean("developer_logging", false);
|
||||
break;
|
||||
case "shell_logging":
|
||||
Logger.logShell = prefs.getBoolean("shell_logging", false);
|
||||
getApplication().shellLogging = prefs.getBoolean("shell_logging", false);
|
||||
break;
|
||||
}
|
||||
setSummary();
|
||||
}
|
||||
|
||||
private void setSummary() {
|
||||
suAccess.setSummary(getResources()
|
||||
.getStringArray(R.array.su_access)[getApplication().suAccessState]);
|
||||
autoRes.setSummary(getResources()
|
||||
.getStringArray(R.array.auto_response)[getApplication().suResponseType]);
|
||||
suNotification.setSummary(getResources()
|
||||
.getStringArray(R.array.su_notification)[getApplication().suNotificationType]);
|
||||
requestTimeout.setSummary(
|
||||
getString(R.string.request_timeout_summary, prefs.getString("su_request_timeout", "10")));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,53 +1,48 @@
|
||||
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.components.Activity;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
public class SplashActivity extends AppCompatActivity {
|
||||
import java.util.List;
|
||||
|
||||
public class SplashActivity extends Activity {
|
||||
|
||||
@Override
|
||||
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));
|
||||
MagiskManager magiskManager = getTopApplication();
|
||||
|
||||
if (Utils.isDarkTheme) {
|
||||
setTheme(R.style.AppTheme_dh);
|
||||
// Init the info and configs and root shell
|
||||
magiskManager.init();
|
||||
|
||||
// Check MagiskHide status
|
||||
List<String> ret = Shell.sh("getprop persist.magisk.hide");
|
||||
boolean started = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
|
||||
|
||||
// Now fire all async tasks
|
||||
new Async.CheckUpdates(magiskManager).exec();
|
||||
new Async.GetBootBlocks(magiskManager).exec();
|
||||
if (magiskManager.magiskHide && !magiskManager.disabled &&
|
||||
magiskManager.magiskVersion > 11 && !started) {
|
||||
new Async.MagiskHide().enable();
|
||||
}
|
||||
|
||||
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();
|
||||
new Async.CheckUpdates().exec();
|
||||
new Async.LoadModules() {
|
||||
new Async.LoadModules(magiskManager) {
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
super.onPostExecute(v);
|
||||
new Async.LoadRepos(getApplicationContext()).exec();
|
||||
new Async.LoadRepos(magiskManager).exec();
|
||||
}
|
||||
}.exec();
|
||||
new Async.LoadApps(getPackageManager()).exec();
|
||||
new Async.LoadApps(magiskManager).exec();
|
||||
|
||||
// Start main activity
|
||||
// Preparation done, now start main activity
|
||||
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
|
@@ -1,12 +1,11 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Fragment;
|
||||
import android.app.FragmentTransaction;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -15,29 +14,23 @@ import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
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;
|
||||
public class StatusFragment extends Fragment implements CallbackEvent.Listener<Void> {
|
||||
|
||||
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,11 +80,11 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
safetyNetStatusText.setText(R.string.safetyNet_check_text);
|
||||
safetyNetStatusText.setTextColor(defaultColor);
|
||||
|
||||
safetyNetDone.isTriggered = false;
|
||||
getApplication().safetyNetDone.isTriggered = false;
|
||||
noDialog = false;
|
||||
|
||||
updateUI();
|
||||
new Async.CheckUpdates().exec();
|
||||
new Async.CheckUpdates(getApplication()).exec();
|
||||
});
|
||||
|
||||
safetyNetContainer.setOnClickListener(view -> {
|
||||
@@ -103,10 +92,10 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
safetyNetContainer.setBackgroundColor(trans);
|
||||
safetyNetIcon.setImageResource(0);
|
||||
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
|
||||
Async.checkSafetyNet(getActivity());
|
||||
Async.checkSafetyNet(getApplication());
|
||||
});
|
||||
|
||||
if (magiskVersion < 0 && Shell.rootAccess() && !noDialog) {
|
||||
if (getApplication().magiskVersion < 0 && Shell.rootAccess() && !noDialog) {
|
||||
noDialog = true;
|
||||
Utils.getAlertDialogBuilder(getActivity())
|
||||
.setTitle(R.string.no_magisk_title)
|
||||
@@ -115,7 +104,7 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
.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) {}
|
||||
@@ -126,84 +115,80 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
|
||||
updateUI();
|
||||
|
||||
if (getApplication().updateCheckDone.isTriggered)
|
||||
updateCheckUI();
|
||||
if (getApplication().safetyNetDone.isTriggered)
|
||||
updateSafetyNetUI();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackHandler.Event event) {
|
||||
if (event == updateCheckDone) {
|
||||
public void onTrigger(CallbackEvent<Void> event) {
|
||||
if (event == getApplication().updateCheckDone) {
|
||||
Logger.dev("StatusFragment: Update Check UI refresh triggered");
|
||||
updateCheckUI();
|
||||
} else if (event == safetyNetDone) {
|
||||
} else if (event == getApplication().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) {
|
||||
updateCheckUI();
|
||||
}
|
||||
if (safetyNetDone.isTriggered) {
|
||||
updateSafetyNetUI();
|
||||
}
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
getApplication().updateCheckDone.register(this);
|
||||
getApplication().safetyNetDone.register(this);
|
||||
getActivity().setTitle(R.string.status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPause() {
|
||||
super.onPause();
|
||||
CallbackHandler.unRegister(updateCheckDone, this);
|
||||
CallbackHandler.unRegister(safetyNetDone, this);
|
||||
public void onStop() {
|
||||
getApplication().updateCheckDone.unRegister(this);
|
||||
getApplication().safetyNetDone.unRegister(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();
|
||||
getApplication().updateMagiskInfo();
|
||||
|
||||
if (magiskVersion < 0) {
|
||||
if (getApplication().magiskVersion < 0) {
|
||||
magiskVersionText.setText(R.string.magisk_version_error);
|
||||
} else if (getApplication().disabled) {
|
||||
magiskVersionText.setText(getString(R.string.magisk_version_disable, getApplication().magiskVersionString));
|
||||
} else {
|
||||
magiskVersionText.setText(getString(R.string.magisk_version, magiskVersionString));
|
||||
magiskVersionText.setText(getString(R.string.magisk_version, getApplication().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 (getApplication().suVersion != null) {
|
||||
color = colorOK;
|
||||
image = R.drawable.ic_check_circle;
|
||||
rootStatusText.setText(R.string.proper_root);
|
||||
rootInfoText.setText(getApplication().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 +199,28 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
private void updateCheckUI() {
|
||||
int image, color;
|
||||
|
||||
if (remoteMagiskVersion < 0) {
|
||||
if (getApplication().remoteMagiskVersion < 0) {
|
||||
color = colorNeutral;
|
||||
image = R.drawable.ic_help;
|
||||
magiskUpdateText.setText(R.string.cannot_check_updates);
|
||||
} else if (remoteMagiskVersion > magiskVersion) {
|
||||
} else if (getApplication().remoteMagiskVersion > getApplication().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, getApplication().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 (getApplication().magiskVersion < 0) {
|
||||
color = colorBad;
|
||||
image = R.drawable.ic_cancel;
|
||||
} else if (getApplication().disabled) {
|
||||
color = colorNeutral;
|
||||
image = R.drawable.ic_cancel;
|
||||
}
|
||||
|
||||
magiskStatusContainer.setBackgroundColor(color);
|
||||
magiskVersionText.setTextColor(color);
|
||||
magiskUpdateText.setTextColor(color);
|
||||
@@ -242,23 +231,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, getApplication().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(getApplication().releaseNoteLink)));
|
||||
})
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.create();
|
||||
|
||||
if (magiskVersion < remoteMagiskVersion && Shell.rootAccess()) {
|
||||
if (getApplication().magiskVersion < getApplication().remoteMagiskVersion && Shell.rootAccess()) {
|
||||
magiskStatusContainer.setOnClickListener(view -> updateMagisk.show());
|
||||
if (!noDialog) {
|
||||
noDialog = true;
|
||||
@@ -270,7 +259,7 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
private void updateSafetyNetUI() {
|
||||
int image, color;
|
||||
safetyNetProgress.setVisibility(View.GONE);
|
||||
switch (SNCheckResult) {
|
||||
switch (getApplication().SNCheckResult) {
|
||||
case -3:
|
||||
color = colorNeutral;
|
||||
image = R.drawable.ic_help;
|
||||
|
92
app/src/main/java/com/topjohnwu/magisk/SuLogFragment.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
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.components.Fragment;
|
||||
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();
|
||||
}
|
||||
}
|
@@ -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.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.components.Fragment;
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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());
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -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);
|
||||
}
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.topjohnwu.magisk.components;
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
|
||||
public class Activity extends AppCompatActivity {
|
||||
|
||||
public MagiskManager getTopApplication() {
|
||||
return (MagiskManager) getApplication();
|
||||
}
|
||||
}
|
@@ -0,0 +1,11 @@
|
||||
package com.topjohnwu.magisk.components;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
|
||||
public class Fragment extends android.support.v4.app.Fragment {
|
||||
|
||||
public MagiskManager getApplication() {
|
||||
return (MagiskManager) getActivity().getApplicationContext();
|
||||
}
|
||||
|
||||
}
|
@@ -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);
|
||||
|
@@ -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.MagiskManager;
|
||||
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,63 +20,57 @@ 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;
|
||||
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 final String FILE_KEY = "RepoMap";
|
||||
|
||||
private static ValueSortedMap<String, Repo> repoMap = new ValueSortedMap<>();
|
||||
private static ValueSortedMap<String, Module> moduleMap = new ValueSortedMap<>();
|
||||
|
||||
|
||||
public static void createModuleMap() {
|
||||
public static void createModuleMap(MagiskManager magiskManager) {
|
||||
Logger.dev("ModuleHelper: Loading modules");
|
||||
|
||||
moduleMap.clear();
|
||||
magiskManager.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);
|
||||
magiskManager.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) {
|
||||
public static void createRepoMap(MagiskManager magiskManager) {
|
||||
Logger.dev("ModuleHelper: Loading repos");
|
||||
|
||||
SharedPreferences prefs = context.getSharedPreferences(FILE_KEY, Context.MODE_PRIVATE);
|
||||
SharedPreferences prefs = magiskManager.prefs;
|
||||
|
||||
repoMap.clear();
|
||||
magiskManager.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,12 +82,12 @@ 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
|
||||
jsonString = WebService.request(
|
||||
context.getString(R.string.url_main), WebService.GET, null, header, false);
|
||||
magiskManager.getString(R.string.url_main), WebService.GET, null, header, false);
|
||||
|
||||
if (!jsonString.isEmpty()) {
|
||||
try {
|
||||
@@ -117,13 +114,13 @@ public class ModuleHelper {
|
||||
try {
|
||||
if (repo == null) {
|
||||
Logger.dev("ModuleHelper: Create new repo " + id);
|
||||
repo = new Repo(context, name, updatedDate);
|
||||
repo = new Repo(magiskManager, name, updatedDate);
|
||||
} else {
|
||||
Logger.dev("ModuleHelper: Update cached repo " + id);
|
||||
repo.update(updatedDate);
|
||||
}
|
||||
if (repo.getId() != null) {
|
||||
repoMap.put(id, repo);
|
||||
magiskManager.repoMap.put(id, repo);
|
||||
}
|
||||
} catch (BaseModule.CacheModException ignored) {}
|
||||
}
|
||||
@@ -133,71 +130,27 @@ public class ModuleHelper {
|
||||
} else {
|
||||
// Use cached if no internet or no updates
|
||||
Logger.dev("ModuleHelper: No updates, use cached");
|
||||
repoMap.putAll(cached);
|
||||
magiskManager.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(magiskManager.repoMap))
|
||||
.putString(ETAG_KEY, etag)
|
||||
.apply();
|
||||
|
||||
Logger.dev("ModuleHelper: Repo load done");
|
||||
}
|
||||
|
||||
public static void getModuleList(List<Module> moduleList) {
|
||||
moduleList.clear();
|
||||
moduleList.addAll(moduleMap.values());
|
||||
public static void clearRepoCache(MagiskManager magiskManager) {
|
||||
SharedPreferences repoMap = magiskManager.getSharedPreferences(FILE_KEY, Context.MODE_PRIVATE);
|
||||
repoMap.edit()
|
||||
.remove(ETAG_KEY)
|
||||
.remove(VERSION_KEY)
|
||||
.apply();
|
||||
magiskManager.repoLoadDone.isTriggered = false;
|
||||
new Async.LoadRepos(magiskManager).exec();
|
||||
Toast.makeText(magiskManager, R.string.repo_cache_cleared, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
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());
|
||||
if (module != null) {
|
||||
if (repo.getVersionCode() > module.getVersionCode()) {
|
||||
update.add(repo);
|
||||
} else {
|
||||
installed.add(repo);
|
||||
}
|
||||
} else {
|
||||
others.add(repo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
package com.topjohnwu.magisk.receivers;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BootReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
context.startService(new Intent(context, BootupIntentService.class));
|
||||
}
|
||||
|
||||
public static class BootupIntentService extends IntentService {
|
||||
|
||||
public BootupIntentService() {
|
||||
super("BootupIntentService");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
MagiskManager magiskManager = (MagiskManager) getApplicationContext();
|
||||
magiskManager.initSuAccess();
|
||||
magiskManager.updateMagiskInfo();
|
||||
List<String> ret = Shell.sh("getprop persist.magisk.hide");
|
||||
boolean started = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
|
||||
if (magiskManager.prefs.getBoolean("magiskhide", false) &&
|
||||
!magiskManager.disabled && !started && magiskManager.magiskVersion > 11) {
|
||||
magiskManager.toast(R.string.start_magiskhide, Toast.LENGTH_LONG);
|
||||
Shell.su(true, Async.MAGISK_HIDE_PATH + "enable",
|
||||
"setprop persist.magisk.hide 1");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -2,8 +2,8 @@ package com.topjohnwu.magisk.receivers;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
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(((MagiskManager) mContext.getApplicationContext()).remoteMagiskVersion));
|
||||
return null;
|
||||
}
|
||||
}.exec();
|
||||
|
@@ -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();
|
||||
}
|
||||
|
51
app/src/main/java/com/topjohnwu/magisk/superuser/Policy.java
Normal 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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,26 @@
|
||||
package com.topjohnwu.magisk.superuser;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.topjohnwu.magisk.components.Activity;
|
||||
|
||||
public class RequestActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Intent intent = getIntent();
|
||||
if (intent == null) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
getTopApplication().initSuConfigs();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setClass(this, SuRequestActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
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.MagiskManager;
|
||||
|
||||
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";
|
||||
|
||||
private MagiskManager magiskManager;
|
||||
|
||||
public SuLogDatabaseHelper(Context context) {
|
||||
super(context, "sulog.db", null, DATABASE_VER);
|
||||
magiskManager = (MagiskManager) context.getApplicationContext();
|
||||
}
|
||||
|
||||
@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 - magiskManager.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;
|
||||
}
|
||||
}
|
@@ -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());
|
||||
}
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
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.MagiskManager;
|
||||
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;
|
||||
|
||||
MagiskManager magiskManager = (MagiskManager) context.getApplicationContext();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
magiskManager.initSuConfigs();
|
||||
|
||||
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 && magiskManager.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);
|
||||
}
|
||||
}
|
||||
}
|
@@ -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.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.MagiskManager;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.components.Activity;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class SuRequestActivity extends Activity implements CallbackEvent.Listener<Policy> {
|
||||
|
||||
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 MagiskManager magiskManager;
|
||||
|
||||
private int uid;
|
||||
private Policy policy;
|
||||
private CountDownTimer timer;
|
||||
private CallbackEvent.Listener<Policy> self;
|
||||
private CallbackEvent<Policy> event = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
pm = getPackageManager();
|
||||
magiskManager = getTopApplication();
|
||||
|
||||
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 (magiskManager.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(magiskManager.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(CallbackEvent<Policy> event) {
|
||||
Policy policy = event.getResult();
|
||||
String response = "socket:DENY";
|
||||
if (policy != null &&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 = magiskManager.uidMap.get(uid);
|
||||
if (event == null) {
|
||||
showRequest = true;
|
||||
event = new CallbackEvent<Policy>() {
|
||||
@Override
|
||||
public void trigger(Policy result) {
|
||||
super.trigger(result);
|
||||
unRegister();
|
||||
magiskManager.uidMap.remove(uid);
|
||||
}
|
||||
};
|
||||
magiskManager.uidMap.put(uid, event);
|
||||
}
|
||||
event.register(self);
|
||||
try {
|
||||
if (showRequest) {
|
||||
policy = new Policy(uid, pm);
|
||||
showRequest();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
event.trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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.MagiskManager;
|
||||
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;
|
||||
@@ -54,105 +51,106 @@ public class Async {
|
||||
|
||||
public static class CheckUpdates extends NormalTask<Void, Void, Void> {
|
||||
|
||||
MagiskManager magiskManager;
|
||||
|
||||
public CheckUpdates(MagiskManager context) {
|
||||
magiskManager = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
String jsonStr = WebService.request(UPDATE_JSON, WebService.GET);
|
||||
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");
|
||||
magiskManager.remoteMagiskVersion = magisk.getDouble("versionCode");
|
||||
magiskManager.magiskLink = magisk.getString("link");
|
||||
magiskManager.releaseNoteLink = magisk.getString("note");
|
||||
} catch (JSONException ignored) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
StatusFragment.updateCheckDone.trigger();
|
||||
magiskManager.updateCheckDone.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkSafetyNet(Context context) {
|
||||
new SafetyNetHelper(context) {
|
||||
public static void checkSafetyNet(MagiskManager magiskManager) {
|
||||
new SafetyNetHelper(magiskManager) {
|
||||
@Override
|
||||
public void handleResults(int i) {
|
||||
StatusFragment.SNCheckResult = i;
|
||||
StatusFragment.safetyNetDone.trigger();
|
||||
magiskManager.SNCheckResult = i;
|
||||
magiskManager.safetyNetDone.trigger();
|
||||
}
|
||||
}.requestTest();
|
||||
}
|
||||
|
||||
public static class LoadModules extends RootTask<Void, Void, Void> {
|
||||
|
||||
protected MagiskManager magiskManager;
|
||||
|
||||
public LoadModules(MagiskManager context) {
|
||||
magiskManager = context;
|
||||
}
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
ModuleHelper.createModuleMap();
|
||||
ModuleHelper.createModuleMap(magiskManager);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
ModulesFragment.moduleLoadDone.trigger();
|
||||
magiskManager.moduleLoadDone.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoadRepos extends NormalTask<Void, Void, Void> {
|
||||
|
||||
private Context mContext;
|
||||
private MagiskManager magiskManager;
|
||||
|
||||
public LoadRepos(Context context) {
|
||||
mContext = context;
|
||||
public LoadRepos(MagiskManager context) {
|
||||
magiskManager = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
ModuleHelper.createRepoMap(mContext);
|
||||
ModuleHelper.createRepoMap(magiskManager);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
ReposFragment.repoLoadDone.trigger();
|
||||
magiskManager.repoLoadDone.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoadApps extends RootTask<Void, Void, LoadApps.Result> {
|
||||
public static class LoadApps extends RootTask<Void, Void, Void> {
|
||||
|
||||
private PackageManager pm;
|
||||
private MagiskManager magiskManager;
|
||||
|
||||
public LoadApps(PackageManager packageManager) {
|
||||
pm = packageManager;
|
||||
public LoadApps(MagiskManager context) {
|
||||
magiskManager = context;
|
||||
}
|
||||
|
||||
@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) {
|
||||
PackageManager pm = magiskManager.getPackageManager();
|
||||
magiskManager.appList = pm.getInstalledApplications(0);
|
||||
for (Iterator<ApplicationInfo> i = magiskManager.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(magiskManager.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);
|
||||
magiskManager.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) {
|
||||
magiskManager.packageLoadDone.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,19 +161,22 @@ public class Async {
|
||||
private String mFilename;
|
||||
protected ProgressDialog progress;
|
||||
private Context mContext;
|
||||
private MagiskManager magiskManager;
|
||||
|
||||
public FlashZIP(Context context, Uri uri, String filename) {
|
||||
mContext = context;
|
||||
magiskManager = (MagiskManager) context.getApplicationContext();
|
||||
mUri = uri;
|
||||
mFilename = filename;
|
||||
}
|
||||
|
||||
public FlashZIP(Context context, Uri uri) {
|
||||
mContext = context;
|
||||
magiskManager = (MagiskManager) context.getApplicationContext();
|
||||
mUri = uri;
|
||||
|
||||
// Try to get the filename ourselves
|
||||
Cursor c = mContext.getContentResolver().query(uri, null, null, null, null);
|
||||
Cursor c = magiskManager.getContentResolver().query(uri, null, null, null, null);
|
||||
if (c != null) {
|
||||
int nameIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
c.moveToFirst();
|
||||
@@ -193,35 +194,37 @@ public class Async {
|
||||
protected void preProcessing() throws Throwable {}
|
||||
|
||||
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);
|
||||
publishProgress(magiskManager.getString(R.string.copying_msg));
|
||||
mCachedFile = new File(magiskManager.getCacheDir().getAbsolutePath() + "/install.zip");
|
||||
if (mCachedFile.exists() && !mCachedFile.delete()) {
|
||||
Logger.error("FlashZip: Error while deleting already existing file");
|
||||
throw new IOException();
|
||||
}
|
||||
try (
|
||||
InputStream in = magiskManager.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
|
||||
@@ -248,12 +251,13 @@ public class Async {
|
||||
return -1;
|
||||
}
|
||||
if (!unzipAndCheck()) return 0;
|
||||
publishProgress(mContext.getString(R.string.zip_install_progress_msg, mFilename));
|
||||
publishProgress(magiskManager.getString(R.string.zip_install_progress_msg, mFilename));
|
||||
ret = Shell.su(
|
||||
"BOOTMODE=true sh " + mCachedFile.getParent() +
|
||||
"/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);
|
||||
@@ -275,12 +279,12 @@ public class Async {
|
||||
progress.dismiss();
|
||||
switch (result) {
|
||||
case -1:
|
||||
Toast.makeText(mContext, mContext.getString(R.string.install_error), Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(mContext, mContext.getString(R.string.manual_install_1, mUri.getPath()), Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(mContext, mContext.getString(R.string.manual_install_2), Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(magiskManager, magiskManager.getString(R.string.install_error), Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(magiskManager, magiskManager.getString(R.string.manual_install_1, mUri.getPath()), Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(magiskManager, magiskManager.getString(R.string.manual_install_2), Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case 0:
|
||||
Toast.makeText(mContext, mContext.getString(R.string.invalid_zip), Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(magiskManager, magiskManager.getString(R.string.invalid_zip), Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case 1:
|
||||
onSuccess();
|
||||
@@ -289,52 +293,66 @@ public class Async {
|
||||
}
|
||||
|
||||
protected void onSuccess() {
|
||||
StatusFragment.updateCheckDone.trigger();
|
||||
new LoadModules().exec();
|
||||
magiskManager.updateCheckDone.trigger();
|
||||
new LoadModules(magiskManager).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> {
|
||||
|
||||
@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(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; setprop persist.magisk.hide 1");
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
exec("disable; setprop persist.magisk.hide 0");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class GetBootBlocks extends RootTask<Void, Void, Void> {
|
||||
|
||||
MagiskManager magiskManager;
|
||||
|
||||
public GetBootBlocks(MagiskManager context) {
|
||||
magiskManager = context;
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
magiskManager.blockList = Shell.su("ls /dev/block | grep mmc");
|
||||
if (magiskManager.bootBlock == null)
|
||||
magiskManager.bootBlock = Utils.detectBootImage();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void aVoid) {
|
||||
InstallFragment.blockDetectionDone.trigger();
|
||||
protected void onPostExecute(Void v) {
|
||||
magiskManager.blockDetectionDone.trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class CallbackEvent<Result> {
|
||||
|
||||
public boolean isTriggered = false;
|
||||
private Result result;
|
||||
private Set<Listener<Result>> listeners;
|
||||
|
||||
public void register(Listener<Result> l) {
|
||||
if (listeners == null)
|
||||
listeners = new HashSet<>();
|
||||
listeners.add(l);
|
||||
}
|
||||
|
||||
public void unRegister() {
|
||||
listeners = null;
|
||||
}
|
||||
|
||||
public void unRegister(Listener<Result> l) {
|
||||
if (listeners != null)
|
||||
listeners.remove(l);
|
||||
}
|
||||
|
||||
public void trigger() {
|
||||
trigger(null);
|
||||
}
|
||||
|
||||
public void trigger(Result r) {
|
||||
result = r;
|
||||
isTriggered = true;
|
||||
if (listeners != null) {
|
||||
for (Listener<Result> listener : listeners)
|
||||
listener.onTrigger(this);
|
||||
}
|
||||
}
|
||||
|
||||
public Result getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public interface Listener<R> {
|
||||
void onTrigger(CallbackEvent<R> event);
|
||||
}
|
||||
}
|
@@ -1,58 +0,0 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class CallbackHandler {
|
||||
|
||||
private static HashMap<Event, HashSet<EventListener>> listeners = new HashMap<>();
|
||||
|
||||
public static void register(Event event, EventListener listener) {
|
||||
HashSet<EventListener> list = listeners.get(event);
|
||||
if (list == null) {
|
||||
list = new HashSet<>();
|
||||
listeners.put(event, list);
|
||||
}
|
||||
list.add(listener);
|
||||
}
|
||||
|
||||
public static void unRegister(Event event, EventListener listener) {
|
||||
HashSet<EventListener> list = listeners.get(event);
|
||||
if (list != null) {
|
||||
list.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private static void triggerCallback(Event event) {
|
||||
HashSet<EventListener> list = listeners.get(event);
|
||||
if (list != null) {
|
||||
for (EventListener listener : list) {
|
||||
listener.onTrigger(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Event {
|
||||
|
||||
public boolean isTriggered = false;
|
||||
private Object result;
|
||||
|
||||
public void trigger() {
|
||||
trigger(null);
|
||||
}
|
||||
|
||||
public void trigger(Object result) {
|
||||
this.result = result;
|
||||
isTriggered = true;
|
||||
triggerCallback(this);
|
||||
}
|
||||
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public interface EventListener {
|
||||
void onTrigger(Event event);
|
||||
}
|
||||
}
|
@@ -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());
|
||||
}
|
@@ -2,36 +2,38 @@ package com.topjohnwu.magisk.utils;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
|
||||
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 (MagiskManager.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 (MagiskManager.devLogging) {
|
||||
Log.d(TAG, "DEBUG: " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void shell(boolean root, String msg) {
|
||||
if (logShell) {
|
||||
if (MagiskManager.shellLogging) {
|
||||
Log.d(root ? "SU" : "SH", msg);
|
||||
}
|
||||
}
|
||||
|
@@ -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>());
|
||||
@@ -130,6 +132,13 @@ public class Shell {
|
||||
process = Runtime.getRuntime().exec("su");
|
||||
STDIN = new DataOutputStream(process.getOutputStream());
|
||||
STDOUT = new StreamGobbler(process.getInputStream(), res);
|
||||
|
||||
// Run the new shell with busybox and proper umask
|
||||
STDIN.write(("umask 022\n").getBytes("UTF-8"));
|
||||
STDIN.flush();
|
||||
STDIN.write(("PATH=`[ -e /dev/busybox ] && echo /dev/busybox || " +
|
||||
"echo /data/busybox`:$PATH\n").getBytes("UTF-8"));
|
||||
STDIN.flush();
|
||||
} catch (IOException err) {
|
||||
return null;
|
||||
}
|
||||
@@ -170,6 +179,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 +193,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;
|
||||
}
|
||||
|
||||
|
@@ -1,66 +1,57 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import android.Manifest;
|
||||
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;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
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 (((MagiskManager) context.getApplicationContext()).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();
|
||||
}
|
||||
|
||||
}
|
@@ -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);
|
||||
}
|
||||
}
|
@@ -1,6 +1,6 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebView;
|
||||
|
@@ -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
|
||||
|
7
app/src/main/jni/CMakeLists.txt
Normal 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})
|
23
app/src/main/jni/jni_glue.c
Normal 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;
|
||||
}
|
@@ -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 {
|
||||
|
9
app/src/main/jni/zipadjust.h
Normal 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
|
13
app/src/main/res/drawable/ic_arrow.xml
Normal 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>
|
@@ -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>
|
||||
|
@@ -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>
|
9
app/src/main/res/drawable/ic_more.xml
Normal 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>
|
9
app/src/main/res/drawable/ic_notifications.xml
Normal 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>
|
@@ -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>
|
9
app/src/main/res/drawable/ic_su_warning.xml
Normal 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>
|
7
app/src/main/res/drawable/ic_superuser.xml
Normal 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>
|
@@ -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"
|
||||
|
117
app/src/main/res/layout/activity_request.xml
Normal 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>
|
251
app/src/main/res/layout/fragment_install.xml
Normal 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>
|
28
app/src/main/res/layout/fragment_log.xml
Normal 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>
|
@@ -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"
|
@@ -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"
|
28
app/src/main/res/layout/fragment_su_log.xml
Normal 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>
|
28
app/src/main/res/layout/fragment_superuser.xml
Normal 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>
|
@@ -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>
|
@@ -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"
|
||||
|
@@ -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>
|
||||
|
173
app/src/main/res/layout/list_item_policy.xml
Normal 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>
|
||||
|
||||
|
@@ -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"/>
|
||||
|
||||
|
145
app/src/main/res/layout/list_item_sulog.xml
Normal 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>
|
||||
|
||||
|
23
app/src/main/res/layout/list_item_sulog_group.xml
Normal 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>
|
@@ -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"
|
||||
|
@@ -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"/>
|
||||
|
||||
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 20 KiB |
@@ -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>
|
||||
|
@@ -1,23 +1,54 @@
|
||||
<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 neueste 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">Neueste Magisk-Version: v%1$.1f</string>
|
||||
|
||||
<string name="uninstall">Deinstallieren</string>
|
||||
<string name="reboot_countdown">Neustart in %1$d</string>
|
||||
|
||||
<!--Root Fragment-->
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Nichts angegeben)</string>
|
||||
@@ -28,78 +59,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 dem 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 & 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 Informationen 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 -->
|
||||
<!--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>
|
||||
|
||||
<!-- Example settings for Data & Sync -->
|
||||
|
||||
<!-- Example settings for Notifications -->
|
||||
<!--Superuser logs-->
|
||||
<string name="pid">PID:\u0020</string>
|
||||
<string name="target_uid">Ziel-UID:\u0020</string>
|
||||
<string name="command">Befehl:\u0020</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -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>
|
||||
|
@@ -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>
|
||||
|
@@ -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 -->
|
||||
|
197
app/src/main/res/values-pl/strings.xml
Normal file
@@ -0,0 +1,197 @@
|
||||
<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">Zrootowany prawidłowo</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">Błąd SafetyNet: Niezgodność profilu CTS</string>
|
||||
<string name="safetyNet_pass">SafetyNet Poprawny</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>
|
||||
|
||||
<string name="uninstall">Odinstaluj</string>
|
||||
<string name="reboot_countdown">Restartuj do %1$d</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">Kod źródłowy</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>
|
||||
<string name="no_magisksu_title">Nie Używaj MagiskSU!</string>
|
||||
<string name="no_magisksu_msg">Jeśli nie masz roota z MagiskSU, używanie samego MagiskHide może nie wystarczyć! Inne metody nie są oficjalnie obsługiwane. Do poprawnego działania SaftyNet potrzebne będą dodatkowe narzędzia (np suhide)</string>
|
||||
<string name="understand">Rozumiem</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 Magisk</string>
|
||||
<string name="settings_disable_summary">Wszystko zostanie wyłączone za wyjątkiem roota (MagiskSU)</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>
|
@@ -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 & 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>
|
||||
|
187
app/src/main/res/values-ru/strings.xml
Normal file
@@ -0,0 +1,187 @@
|
||||
<resources>
|
||||
<!--Welcome Activity-->
|
||||
<string name="navigation_drawer_open">Открыть меню навигации</string>
|
||||
<string name="navigation_drawer_close">Закрыть меню навигации</string>
|
||||
<string name="magiskhide">Magisk Hide</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>
|
||||
<string name="install">Установка</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="magisk_version">Установлен Magisk v%1$s</string>
|
||||
<string name="magisk_version_disable">Magisk v%1$s выключен</string>
|
||||
<string name="magisk_version_error">Magisk не установлен</string>
|
||||
|
||||
<string name="checking_for_updates">Проверка обновлений…</string>
|
||||
<string name="magisk_update_available">Доступен Magisk v%1$.1f!</string>
|
||||
<string name="cannot_check_updates">Невозможно проверить обновления, нет соединения?</string>
|
||||
<string name="up_to_date">Установлена последняя версия %1$s</string>
|
||||
<string name="root_error">Рут есть, но нет разрешения, не разрешено?</string>
|
||||
<string name="not_rooted">Нет рута</string>
|
||||
<string name="proper_root">Рут получен правильно</string>
|
||||
<string name="safetyNet_check_text">Нажмите для запуска проверки SafetyNet</string>
|
||||
<string name="checking_safetyNet_status">Проверка статуса SafetyNet…</string>
|
||||
<string name="safetyNet_connection_failed">Невозможно соединиться с API Google</string>
|
||||
<string name="safetyNet_connection_suspended">Соединение с API Google было приостановлено</string>
|
||||
<string name="safetyNet_error">Невозможно выполнить проверку SafetyNet, нет соединения?</string>
|
||||
<string name="safetyNet_fail">SafetyNet не пройден: несовпадение профиля CTS</string>
|
||||
<string name="safetyNet_pass">SafetyNet пройден</string>
|
||||
<string name="root_info_warning">Функциональность значительно ограничена</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="auto_detect">"(Авто) %1$s"</string>
|
||||
<string name="boot_image_title">Местоположение образа Boot</string>
|
||||
<string name="detect_button">Определить</string>
|
||||
<string name="advanced_settings_title">Дополнительные настройки</string>
|
||||
<string name="keep_force_encryption">Оставить шифрование</string>
|
||||
<string name="keep_dm_verity">Оставить dm-verity</string>
|
||||
<string name="current_magisk_title">Установленная версия Magisk: v%1$s</string>
|
||||
<string name="install_magisk_title">Последняя версия Magisk: v%1$.1f</string>
|
||||
<string name="uninstall">Удалить</string>
|
||||
<string name="reboot_countdown">Перезагрузка через %1$d</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Нет информации)</string>
|
||||
<string name="no_modules_found">Модули не найдены</string>
|
||||
<string name="update_file_created">Модуль будет обновлён при перезагрузке</string>
|
||||
<string name="remove_file_created">Модуль будет удалён при перезагрузке</string>
|
||||
<string name="remove_file_deleted">Модуль не будет удалён при перезагрузке</string>
|
||||
<string name="disable_file_created">Модуль будет выключён при перезагрузке</string>
|
||||
<string name="disable_file_removed">Модуль будет включён при перезагрузке</string>
|
||||
<string name="author">Автор: %1$s</string>
|
||||
<string name="fab_flash_zip">Прошить модуль из zip-архива</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">Доступно обновление</string>
|
||||
<string name="installed">Установлен</string>
|
||||
<string name="not_installed">Не установлен</string>
|
||||
<string name="changelog">Изменения</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">Сохранить на SD-карту</string>
|
||||
<string name="menuReload">Обновить</string>
|
||||
<string name="menuClearLog">Очистить</string>
|
||||
<string name="logs_cleared">Лог успешно очищен</string>
|
||||
<string name="log_is_empty">Лог пуст</string>
|
||||
<string name="logs_save_failed">Не удалось сохранить лог на SD-карту:</string>
|
||||
|
||||
<!--About Activity-->
|
||||
<string name="about">О приложении</string>
|
||||
<string name="app_developers">Основные разработчики</string>
|
||||
<string name="app_developers_"><![CDATA[Приложение создано <a href="https://github.com/topjohnwu">topjohnwu</a> совместно с <a href="https://github.com/d8ahazard">Digitalhigh</a> и <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
|
||||
<string name="app_changelog">Список изменений</string>
|
||||
<string name="translators">Exalm</string>
|
||||
<string name="app_version">Версия</string>
|
||||
<string name="app_source_code">Исходный код</string>
|
||||
<string name="donation">Пожертвовать</string>
|
||||
<string name="app_translators">Переводчики</string>
|
||||
<string name="support_thread">Страница поддержки</string>
|
||||
|
||||
<!--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>
|
||||
<string name="goto_install">Перейти в раздел «Установка»</string>
|
||||
<string name="download_file_error">Ошибка при скачивании файла</string>
|
||||
<string name="install_error">Ошибка при установке!</string>
|
||||
<string name="manual_install_1">Zip-файл помещён в %1$s</string>
|
||||
<string name="manual_install_2">Прошейте руками через рекавери</string>а
|
||||
<string name="invalid_zip">Этот архив не содержит модуль Magisk!!</string>
|
||||
<string name="reboot_title">Установка успешна!</string>
|
||||
<string name="reboot_msg">Вы хотите перезагрузиться?</string>
|
||||
<string name="reboot">Перезагрузка</string>
|
||||
<string name="copying_msg">Копирование архива во временную директорию</string>
|
||||
<string name="zip_install_progress_title">Установка</string>
|
||||
<string name="zip_install_unzip_zip_msg">Распаковка zip-файла…</string>
|
||||
<string name="zip_install_process_zip_msg">Обработка zip-файла…</string>
|
||||
<string name="zip_install_progress_msg">"Установка %1$s…"</string>
|
||||
<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>
|
||||
<string name="no_magisksu_title">MagiskSU не используется!</string>
|
||||
<string name="no_magisksu_msg">Если рут получен не через MagiskSU, использования MagiskHide может не хватить!\nЭто официально не поддерживается, и вам могут понадобиться дополнительные инструменты (например, suhide), чтобы пройти SafetyNet.</string>
|
||||
<string name="understand">Я понимаю</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">Основные</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_disable_title">Отключить Magisk</string>
|
||||
<string name="settings_disable_summary">Будет выключено все, кроме рута (MagiskSU)</string>
|
||||
<string name="settings_magiskhide_summary">Скрыть Magisk от различных проверок</string>
|
||||
<string name="settings_busybox_title">Включить BusyBox</string>
|
||||
<string name="settings_busybox_summary">Примонтировать встроенный busybox из Magisk в xbin</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>
|
||||
<string name="settings_developer_logging_summary">Нажмите, чтобы включить подробную запись</string>
|
||||
<string name="settings_shell_logging_title">Включить подробное логгирование команд оболочки</string>
|
||||
<string name="settings_shell_logging_summary">Нажмите, чтобы включить запись всех команд оболочки и их вывод</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>
|
||||
|
||||
</resources>
|
@@ -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>
|
||||
|
@@ -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>
|
@@ -20,4 +20,6 @@
|
||||
<color name="dh_icons">#dedede</color>
|
||||
<color name="dh_divider">#313131</color>
|
||||
|
||||
<color name="su_request_background">#e0e0e0</color>
|
||||
|
||||
</resources>
|