1
mirror of https://github.com/topjohnwu/Magisk synced 2025-11-01 12:30:51 +01:00

Compare commits

...

53 Commits

Author SHA1 Message Date
topjohnwu
0a0ad9a184 Bump to 4.3.1 2017-03-31 13:17:58 +08:00
topjohnwu
234bead59e Bump version 2017-03-31 06:58:47 +08:00
Primokorn
76de310986 Create french strings.xml
Hope it's not too late for the update :)
2017-03-31 03:23:23 +08:00
topjohnwu
817f050bcd Say goodbye to old modules 2017-03-30 06:52:18 +08:00
topjohnwu
60ae685d1e Change disable to Core Only Mode 2017-03-30 05:16:50 +08:00
Wang Han
4c7bdbb284 Fix crashing when selecting release notes on some devices 2017-03-26 23:55:11 +08:00
topjohnwu
435251ca41 Bump version 2017-03-20 06:24:59 +08:00
topjohnwu
324a0dd38f Update uninstall script 2017-03-20 04:17:04 +08:00
topjohnwu
cc77d93918 Fix string.xml(vi) 2017-03-20 03:38:24 +08:00
Nguyễn Thanh Tài
0ea7d8bd8c Added Vietnamese translation 2017-03-20 03:12:03 +08:00
topjohnwu
849b217143 Fix build issues 2017-03-16 14:08:40 +08:00
Fabio
9af6efba59 Update Italian Translation [2/2] 2017-03-16 13:40:52 +08:00
Fatih Fırıncı
079d6f06ef Added turkish language
Please merge it
2017-03-16 13:40:43 +08:00
gargamelek
9cf0757689 Added czech translation 2017-03-16 13:40:30 +08:00
c727
b54c438948 update strings-de 2017-03-16 13:40:10 +08:00
linar10
c3ff4bfdad Update strings pl 2017-03-16 13:39:49 +08:00
topjohnwu
5d62e066e2 Bump version 2017-02-22 05:06:19 +08:00
topjohnwu
e94219c5a3 Add notification settings 2017-02-22 04:58:03 +08:00
topjohnwu
8ed9634adf Fix Samsung crash 2017-02-22 04:13:21 +08:00
topjohnwu
0aefa9599f Version bump 2017-02-21 03:52:35 +08:00
c727
e279cf0575 update strings-de 2017-02-20 13:40:01 -06:00
topjohnwu
a3f0ef8e77 Many improvements and bug fixes
Close #114
2017-02-21 03:38:37 +08:00
topjohnwu
8eba05ed4a Potentially fix Samsung crash and change colors 2017-02-20 20:11:07 +08:00
topjohnwu
2f78155723 Bump version 2017-02-19 10:49:47 +08:00
topjohnwu
6785221479 Small refinements and bugfixes
Close #109
2017-02-19 10:14:29 +08:00
topjohnwu
9bc410dd3d Add MarkDown styles 2017-02-18 04:35:51 +08:00
gh2923
2491ab6bf9 Update Simplified Chinese Translation 2017-02-17 10:56:44 -06:00
topjohnwu
f615ed40cd Several refinements 2017-02-17 14:07:15 +08:00
linar10
430f2cafc1 Update strings.xml 2017-02-16 23:27:51 -06:00
Deiki-kun
0ad049da88 Updated and corrected Spanish strings.xml 2017-02-16 23:27:39 -06:00
c727
2c7691567b Update strings-de 2017-02-16 23:27:22 -06:00
topjohnwu
1d70d0fe94 Don't show notification again if coming from notification 2017-02-17 09:26:27 +08:00
topjohnwu
ac44f05811 Resource cleanup 2017-02-17 09:03:40 +08:00
topjohnwu
d99252f394 Add update notification 2017-02-17 08:51:51 +08:00
topjohnwu
b58c7ba7c5 Add download button to repo, close #99 2017-02-16 17:50:36 +08:00
topjohnwu
8c5acd1a0a Add traditional Chinese 2017-02-16 17:09:11 +08:00
linar10
b9b1ebf18c Update strings.xml 2017-02-16 01:44:37 -06:00
lilymaniac
8ca132cef0 Add Korean translation
Change-Id: Ie5b9ee02dc179c99b1ff5c50e5ce046cc2f2522e
Signed-off-by: lilymaniac <lilymaniac@outlook.com>
2017-02-16 01:43:46 -06:00
topjohnwu
a03bb90754 Use README.md in details for repo 2017-02-16 05:48:26 +08:00
topjohnwu
d1c939f48a Use temporary files to process zips
Fix #96
2017-02-15 23:46:50 +08:00
gh2923
21b11f1b48 Update Simplified Chinese Translation 2017-02-15 08:44:45 +08:00
topjohnwu
23c84a7803 Massive Zip flashing refactoring 2017-02-15 05:25:24 +08:00
topjohnwu
f9ab060403 Fix su request crashing 2017-02-15 05:07:14 +08:00
topjohnwu
df7a5bf149 Redo styling 2017-02-14 16:35:03 +08:00
topjohnwu
c4afa069df Add custom AlertDialog 2017-02-13 23:11:50 +08:00
topjohnwu
1bfafdb44f Don't reload ApplicationInfo list
Fix #94
2017-02-13 04:00:45 +08:00
topjohnwu
1ef5bd7076 Remove URL in resources 2017-02-13 03:16:39 +08:00
linar10
29176fa4f4 Update strings-pl 2017-02-13 03:14:24 +08:00
topjohnwu
958c95732b Move AboutCardRow to components package 2017-02-13 03:13:24 +08:00
topjohnwu
44b0d4127c Remove GSON and switch to database 2017-02-12 23:27:20 +08:00
topjohnwu
1418ec2416 Remove module helper 2017-02-12 20:53:41 +08:00
topjohnwu
b51978f51c Move asynctasks to seperate package 2017-02-12 19:49:46 +08:00
topjohnwu
b07361580a Contexts are different: Make context clearer 2017-02-12 05:02:18 +08:00
97 changed files with 5057 additions and 2335 deletions

View File

@@ -8,8 +8,8 @@ android {
applicationId "com.topjohnwu.magisk"
minSdkVersion 21
targetSdkVersion 25
versionCode 22
versionName "4.1"
versionCode 28
versionName "4.3.1"
jackOptions {
enabled true
jackInProcess true
@@ -47,15 +47,14 @@ repositories {
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
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.android.support:recyclerview-v7:25.3.1'
compile 'com.android.support:cardview-v7:25.3.1'
compile 'com.android.support:design:25.3.1'
compile 'com.android.support:support-v4:25.3.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 'us.feras.mdv:markdownview:1.1.0'
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'

View File

@@ -16,27 +16,6 @@
# public *;
#}
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-keep class sun.misc.Unsafe { *; }
-keep class com.google.gson.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep class com.topjohnwu.magisk.module.** { *; }
-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)
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
-keep class android.support.v7.internal.** { *; }
-keep interface android.support.v7.internal.** { *; }
-keep class android.support.v7.** { *; }

View File

@@ -8,6 +8,7 @@
<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.VIBRATE" />
<application
android:name=".MagiskManager"
@@ -60,7 +61,12 @@
</intent-filter>
</receiver>
<service android:name=".receivers.BootReceiver$BootupIntentService" />
<service android:name=".services.BootupIntentService" />
<service
android:name=".services.UpdateCheckService"
android:permission="android.permission.BIND_JOB_SERVICE"
android:exported="true" />
<provider
android:name="android.support.v4.content.FileProvider"

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,21 +1,20 @@
#!/system/bin/sh
[ -z $BOOTMODE ] && BOOTMODE=false
TMPDIR=/tmp
($BOOTMODE) && TMPDIR=/dev/tmp
BINDIR=/data/magisk
CHROMEDIR=$BINDIR/chromeos
# This path should work in any cases
TMPDIR=/dev/tmp
NEWBOOT=$TMPDIR/boottmp/new-boot.img
UNPACKDIR=$TMPDIR/boottmp/bootunpack
RAMDISK=$TMPDIR/boottmp/ramdisk
BOOTTMP=$TMPDIR/boottmp
MAGISKBIN=/data/magisk
CHROMEDIR=$MAGISKBIN/chromeos
SYSTEMLIB=/system/lib
[ -d /system/lib64 ] && SYSTEMLIB=/system/lib64
ui_print() {
echo "$1"
# Default permissions
umask 022
ui_print_wrapper() {
type ui_print >/dev/null && ui_print "$1" || echo "$1"
}
grep_prop() {
@@ -25,7 +24,7 @@ grep_prop() {
if [ -z "$FILES" ]; then
FILES='/system/build.prop'
fi
cat $FILES 2>/dev/null | sed -n $REGEX | head -n 1
cat $FILES 2>/dev/null | sed -n "$REGEX" | head -n 1
}
find_boot_image() {
@@ -42,109 +41,93 @@ find_boot_image() {
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
}
# Environments
# Set permissions
chmod -R 755 $CHROMEDIR/futility $BINDIR
chmod -R 755 $CHROMEDIR/futility $MAGISKBIN 2>/dev/null
# Temporary busybox for installation
mkdir -p $TMPDIR/busybox
$MAGISKBIN/busybox --install -s $TMPDIR/busybox
rm -f $TMPDIR/busybox/su $TMPDIR/busybox/sh $TMPDIR/busybox/reboot
PATH=$TMPDIR/busybox:$PATH
# Find the boot image
find_boot_image
if [ -z "$BOOTIMAGE" ]; then
ui_print "! Unable to detect boot image"
ui_print_wrapper "! Unable to detect boot image"
exit 1
fi
ui_print "- Found Boot Image: $BOOTIMAGE"
ui_print_wrapper "- 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
rm -rf $BOOTTMP 2>/dev/null
mkdir -p $BOOTTMP
cd $BOOTTMP
ui_print_wrapper "- Unpacking boot image"
LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --unpack $BOOTIMAGE
if [ $? -ne 0 ]; then
ui_print_wrapper "! Unable to unpack boot image"
exit 1
fi
# First unpack the boot image
unpack_boot $BOOTIMAGE
# Update our previous backup to new format if exists
if [ -f /data/stock_boot.img ]; then
SHA1=`LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --sha1 /data/stock_boot.img | tail -n 1`
STOCKDUMP=/data/stock_boot_${SHA1}.img
mv /data/stock_boot.img $STOCKDUMP
LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --compress $STOCKDUMP
fi
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/. .
# Detect boot image state
LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --cpio-test ramdisk.cpio
case $? in
0 )
ui_print_wrapper "! Magisk is not installed!"
ui_print_wrapper "! Nothing to uninstall"
exit
;;
1 )
# Find SHA1 of stock boot image
if [ -z $SHA1 ]; then
LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --cpio-extract ramdisk.cpio init.magisk.rc init.magisk.rc
SHA1=`grep_prop "# STOCKSHA1" init.magisk.rc`
[ ! -z $SHA1 ] && STOCKDUMP=/data/stock_boot_${SHA1}.img
rm -f init.magisk.rc
fi
rm -f magisk sbin/init.magisk.rc sbin/magic_mask.sh
repack_boot
fi
if [ -f ${STOCKDUMP}.gz ]; then
ui_print_wrapper "- Boot image backup found!"
LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --decompress ${STOCKDUMP}.gz stock_boot.img
else
ui_print_wrapper "! Boot image backup unavailable"
ui_print_wrapper "- Restoring ramdisk with backup"
LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --cpio-restore ramdisk.cpio
LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --repack $BOOTIMAGE stock_boot.img
fi
;;
2 )
ui_print_wrapper "- SuperSU patched image detected"
LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --cpio-restore ramdisk.cpio
LD_LIBRARY_PATH=$SYSTEMLIB $MAGISKBIN/magiskboot --repack $BOOTIMAGE stock_boot.img
;;
esac
# Sign chromeos boot
if [ -f chromeos ]; then
echo > config
echo > bootloader
LD_LIBRARY_PATH=$SYSTEMLIB $CHROMEDIR/futility vbutil_kernel --pack stock_boot.img.signed --keyblock $CHROMEDIR/kernel.keyblock --signprivate $CHROMEDIR/kernel_data_key.vbprivk --version 1 --vmlinuz stock_boot.img --config config --arch arm --bootloader bootloader --flags 0x1
rm -f stock_boot.img
mv stock_boot.img.signed stock_boot.img
fi
chmod 644 $NEWBOOT
ui_print "- Flashing stock/reverted image"
ui_print_wrapper "- Flashing stock/reverted image"
[ ! -L "$BOOTIMAGE" ] && dd if=/dev/zero of=$BOOTIMAGE bs=4096 2>/dev/null
dd if=$NEWBOOT of=$BOOTIMAGE bs=4096
dd if=stock_boot.img of=$BOOTIMAGE bs=4096
ui_print "- Removing Magisk files"
ui_print_wrapper "- 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
$BOOTMODE && reboot

View File

@@ -1,12 +1,12 @@
package com.topjohnwu.magisk;
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.AlertDialog;
import android.support.v7.widget.Toolbar;
import android.text.Html;
import android.text.Spanned;
@@ -16,9 +16,10 @@ import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import com.topjohnwu.magisk.components.AboutCardRow;
import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
import java.io.IOException;
import java.io.InputStream;
@@ -46,8 +47,8 @@ public class AboutActivity extends Activity {
super.onCreate(savedInstanceState);
String theme = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("theme", "");
Logger.dev("AboutActivity: Theme is " + theme);
if (getTopApplication().isDarkTheme) {
setTheme(R.style.AppTheme_dh);
if (getApplicationContext().isDarkTheme) {
setTheme(R.style.AppTheme_Dark);
}
setContentView(R.layout.activity_about);
ButterKnife.bind(this);
@@ -85,7 +86,7 @@ public class AboutActivity extends Activity {
result = Html.fromHtml(changes);
}
appChangelog.setOnClickListener(v -> {
AlertDialog d = Utils.getAlertDialogBuilder(this)
AlertDialog d = new AlertDialogBuilder(this)
.setTitle(R.string.app_changelog)
.setMessage(result)
.setPositiveButton(android.R.string.ok, null)
@@ -104,7 +105,7 @@ public class AboutActivity extends Activity {
} else {
result = Html.fromHtml(getString(R.string.app_developers_));
}
AlertDialog d = Utils.getAlertDialogBuilder(this)
AlertDialog d = new AlertDialogBuilder(this)
.setTitle(R.string.app_developers)
.setMessage(result)
.setPositiveButton(android.R.string.ok, null)

File diff suppressed because it is too large Load Diff

View File

@@ -15,8 +15,8 @@ import android.view.ViewGroup;
import android.widget.SearchView;
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import com.topjohnwu.magisk.asyncs.MagiskHide;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.CallbackEvent;
import com.topjohnwu.magisk.utils.Logger;
@@ -50,7 +50,7 @@ public class MagiskHideFragment extends Fragment implements CallbackEvent.Listen
PackageManager packageManager = getActivity().getPackageManager();
mSwipeRefreshLayout.setRefreshing(true);
mSwipeRefreshLayout.setOnRefreshListener(() -> new Async.LoadApps(getApplication()).exec());
mSwipeRefreshLayout.setOnRefreshListener(() -> new MagiskHide(getActivity()).list());
appAdapter = new ApplicationAdapter(packageManager);
recyclerView.setAdapter(appAdapter);
@@ -71,8 +71,9 @@ public class MagiskHideFragment extends Fragment implements CallbackEvent.Listen
}
};
if (getApplication().packageLoadDone.isTriggered)
onTrigger(getApplication().packageLoadDone);
if (getApplication().magiskHideDone.isTriggered) {
onTrigger(getApplication().magiskHideDone);
}
return view;
}
@@ -88,12 +89,12 @@ public class MagiskHideFragment extends Fragment implements CallbackEvent.Listen
public void onStart() {
super.onStart();
getActivity().setTitle(R.string.magiskhide);
getApplication().packageLoadDone.register(this);
getApplication().magiskHideDone.register(this);
}
@Override
public void onStop() {
getApplication().packageLoadDone.unRegister(this);
getApplication().magiskHideDone.unRegister(this);
super.onStop();
}

View File

@@ -24,8 +24,9 @@ import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.topjohnwu.magisk.asyncs.SerialTask;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
@@ -121,12 +122,12 @@ public class MagiskLogFragment extends Fragment {
new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500);
}
} else {
Snackbar.make(txtLog, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
SnackbarMaker.make(txtLog, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
}
}
}
public class LogManager extends Async.RootTask<Object, Void, Object> {
public class LogManager extends SerialTask<Object, Void, Object> {
int mode;
File targetFile;
@@ -150,7 +151,7 @@ public class MagiskLogFragment extends Fragment {
case 1:
Shell.su("echo > " + MAGISK_LOG);
Snackbar.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
return "";
case 2:
@@ -161,8 +162,9 @@ public class MagiskLogFragment extends Fragment {
return false;
}
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
return false;
}
Calendar now = Calendar.getInstance();
String filename = String.format(
@@ -174,8 +176,9 @@ public class MagiskLogFragment extends Fragment {
targetFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/MagiskManager/" + filename);
if ((!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs())
|| (targetFile.exists() && !targetFile.delete()))
|| (targetFile.exists() && !targetFile.delete())) {
return false;
}
List<String> in = Utils.readFile(MAGISK_LOG);
@@ -213,10 +216,11 @@ public class MagiskLogFragment extends Fragment {
break;
case 2:
bool = (boolean) o;
if (bool)
if (bool) {
Toast.makeText(getActivity(), targetFile.toString(), Toast.LENGTH_LONG).show();
else
} else {
Toast.makeText(getActivity(), getString(R.string.logs_save_failed), Toast.LENGTH_LONG).show();
}
break;
}
}

View File

@@ -22,21 +22,24 @@ 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";
public static final String MAGISK_HIDE_PATH = "/magisk/.core/magiskhide/";
public static final String TMP_FOLDER_PATH = "/dev/tmp";
public static final String MAGISK_PATH = "/magisk";
public static final String INTENT_SECTION = "section";
// Events
public final CallbackEvent<Void> blockDetectionDone = new CallbackEvent<>();
public final CallbackEvent<Void> packageLoadDone = new CallbackEvent<>();
public final CallbackEvent<Void> magiskHideDone = 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<>();
public final SparseArray<CallbackEvent<Policy>> uidSuRequest = new SparseArray<>();
// Info
public double magiskVersion;
public String magiskVersionString = "(none)";
public String magiskVersionString;
public double remoteMagiskVersion = -1;
public String magiskLink;
public String releaseNoteLink;
@@ -44,7 +47,8 @@ public class MagiskManager extends Application {
public String bootBlock = null;
public boolean isSuClient = false;
public String suVersion = null;
public boolean disabled = false;
public boolean disabled;
public boolean magiskHideStarted;
// Data
public ValueSortedMap<String, Repo> repoMap;
@@ -59,6 +63,8 @@ public class MagiskManager extends Application {
public boolean magiskHide;
public boolean isDarkTheme;
public boolean updateNotification;
public boolean busybox;
public int suRequestTimeout;
public int suLogTimeout = 14;
public int suAccessState;
@@ -88,6 +94,9 @@ public class MagiskManager extends Application {
devLogging = prefs.getBoolean("developer_logging", false);
shellLogging = prefs.getBoolean("shell_logging", false);
magiskHide = prefs.getBoolean("magiskhide", false);
updateNotification = prefs.getBoolean("notification", true);
// Always start a new root shell manually, just for safety
Shell.init();
updateMagiskInfo();
initSuAccess();
initSuConfigs();
@@ -95,7 +104,8 @@ public class MagiskManager extends Application {
prefs.edit()
.putBoolean("dark_theme", isDarkTheme)
.putBoolean("magiskhide", magiskHide)
.putBoolean("busybox", Utils.commandExists("busybox"))
.putBoolean("notification", updateNotification)
.putBoolean("busybox", busybox)
.putBoolean("hosts", new File("/magisk/.core/hosts").exists())
.putBoolean("disable", Utils.itemExist(MAGISK_DISABLE_FILE))
.putString("su_request_timeout", String.valueOf(suRequestTimeout))
@@ -119,9 +129,9 @@ public class MagiskManager extends Application {
}
if (isSuClient) {
ret = Shell.sh("getprop persist.sys.root_access");
if (Utils.isValidShellResponse(ret))
if (Utils.isValidShellResponse(ret)) {
suAccessState = Integer.parseInt(ret.get(0));
else {
} else {
Shell.su(true, "setprop persist.sys.root_access 3");
suAccessState = 3;
}
@@ -141,12 +151,28 @@ public class MagiskManager extends Application {
magiskVersion = Double.POSITIVE_INFINITY;
}
}
ret = Shell.sh("getprop persist.magisk.busybox");
try {
busybox = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
} catch (NumberFormatException e) {
busybox = false;
}
ret = Shell.sh("getprop ro.magisk.disable");
try {
disabled = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
} catch (NumberFormatException e) {
disabled = false;
}
ret = Shell.sh("getprop persist.magisk.hide");
try {
magiskHideStarted = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
} catch (NumberFormatException e) {
magiskHideStarted = false;
}
if (magiskHideStarted) {
magiskHide = true;
}
}

View File

@@ -7,7 +7,6 @@ import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.design.widget.NavigationView;
import android.support.v4.app.ActivityCompat;
@@ -32,6 +31,7 @@ public class MainActivity extends Activity
private final Handler mDrawerHandler = new Handler();
private SharedPreferences prefs;
private int mDrawerItem;
@BindView(R.id.toolbar) Toolbar toolbar;
@BindView(R.id.drawer_layout) DrawerLayout drawer;
@@ -42,10 +42,10 @@ public class MainActivity extends Activity
@Override
protected void onCreate(final Bundle savedInstanceState) {
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
prefs = getApplicationContext().prefs;
if (getTopApplication().isDarkTheme) {
setTheme(R.style.AppTheme_dh);
if (getApplicationContext().isDarkTheme) {
setTheme(R.style.AppTheme_Dark);
}
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
@@ -76,31 +76,37 @@ public class MainActivity extends Activity
drawer.addDrawerListener(toggle);
toggle.syncState();
navigate(R.id.status);
if (savedInstanceState == null)
navigate(getIntent().getStringExtra(MagiskManager.INTENT_SECTION));
navigationView.setNavigationItemSelectedListener(this);
getTopApplication().reloadMainActivity.register(this);
getApplicationContext().reloadMainActivity.register(this);
getApplicationContext().updateCheckDone.register(this);
}
@Override
protected void onResume() {
super.onResume();
getTopApplication().updateCheckDone.register(this);
// if (getTopApplication().updateCheckDone.isTriggered)
// onTrigger(getTopApplication().updateCheckDone);
checkHideSection();
}
@Override
protected void onPause() {
getTopApplication().updateCheckDone.unRegister(this);
super.onPause();
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
navigate(savedInstanceState.getInt(MagiskManager.INTENT_SECTION, R.id.status));
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(MagiskManager.INTENT_SECTION, mDrawerItem);
}
@Override
protected void onDestroy() {
getTopApplication().reloadMainActivity.unRegister(this);
getApplicationContext().reloadMainActivity.unRegister(this);
getApplicationContext().updateCheckDone.unRegister(this);
super.onDestroy();
}
@@ -122,28 +128,68 @@ public class MainActivity extends Activity
@Override
public void onTrigger(CallbackEvent<Void> event) {
if (event == getTopApplication().updateCheckDone) {
Menu menu = navigationView.getMenu();
menu.findItem(R.id.install).setVisible(
getTopApplication().remoteMagiskVersion > 0 && Shell.rootAccess());
} else if (event == getTopApplication().reloadMainActivity) {
if (event == getApplicationContext().reloadMainActivity) {
recreate();
} else if (event == getApplicationContext().updateCheckDone) {
checkHideSection();
}
}
private void checkHideSection() {
Menu menu = navigationView.getMenu();
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);
menu.findItem(R.id.magiskhide).setVisible(
Shell.rootAccess() && getApplicationContext().magiskVersion >= 8
&& prefs.getBoolean("magiskhide", false));
menu.findItem(R.id.modules).setVisible(
Shell.rootAccess() && getApplicationContext().magiskVersion >= 4);
menu.findItem(R.id.downloads).setVisible(
Shell.rootAccess() && getApplicationContext().magiskVersion >= 4);
menu.findItem(R.id.log).setVisible(Shell.rootAccess());
menu.findItem(R.id.superuser).setVisible(
Shell.rootAccess() && getApplicationContext().isSuClient);
menu.findItem(R.id.install).setVisible(getApplicationContext().remoteMagiskVersion > 0);
}
public void navigate(String item) {
int itemId = R.id.status;
if (item != null) {
switch (item) {
case "status":
itemId = R.id.status;
break;
case "install":
itemId = R.id.install;
break;
case "superuser":
itemId = R.id.superuser;
break;
case "modules":
itemId = R.id.modules;
break;
case "downloads":
itemId = R.id.downloads;
break;
case "magiskhide":
itemId = R.id.magiskhide;
break;
case "log":
itemId = R.id.log;
break;
case "settings":
itemId = R.id.settings;
break;
case "about":
itemId = R.id.app_about;
break;
}
}
navigate(itemId);
}
public void navigate(int itemId) {
int bak = mDrawerItem;
mDrawerItem = itemId;
navigationView.setCheckedItem(itemId);
switch (itemId) {
case R.id.status:
displayFragment(new StatusFragment(), "status", true);
@@ -165,13 +211,14 @@ public class MainActivity extends Activity
break;
case R.id.log:
displayFragment(new LogFragment(), "log", false);
toolbar.setElevation(0);
break;
case R.id.settings:
startActivity(new Intent(this, SettingsActivity.class));
mDrawerItem = bak;
break;
case R.id.app_about:
startActivity(new Intent(this, AboutActivity.class));
mDrawerItem = bak;
break;
}
}
@@ -182,5 +229,6 @@ public class MainActivity extends Activity
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);
else toolbar.setElevation(0);
}
}

View File

@@ -14,9 +14,10 @@ import android.widget.TextView;
import com.github.clans.fab.FloatingActionButton;
import com.topjohnwu.magisk.adapters.ModulesAdapter;
import com.topjohnwu.magisk.asyncs.FlashZip;
import com.topjohnwu.magisk.asyncs.LoadModules;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.module.Module;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.CallbackEvent;
import com.topjohnwu.magisk.utils.Logger;
@@ -53,7 +54,7 @@ public class ModulesFragment extends Fragment implements CallbackEvent.Listener<
mSwipeRefreshLayout.setOnRefreshListener(() -> {
recyclerView.setVisibility(View.GONE);
new Async.LoadModules(getApplication()).exec();
new LoadModules(getActivity()).exec();
});
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@@ -86,7 +87,7 @@ public class ModulesFragment extends Fragment implements CallbackEvent.Listener<
if (requestCode == FETCH_ZIP_CODE && resultCode == Activity.RESULT_OK && data != null) {
// Get the URI of the selected file
final Uri uri = data.getData();
new Async.FlashZIP(getActivity(), uri).exec();
new FlashZip(getActivity(), uri).exec();
}
}

View File

@@ -15,10 +15,11 @@ import android.widget.TextView;
import com.topjohnwu.magisk.adapters.ReposAdapter;
import com.topjohnwu.magisk.adapters.SimpleSectionedRecyclerViewAdapter;
import com.topjohnwu.magisk.asyncs.LoadRepos;
import com.topjohnwu.magisk.asyncs.ParallelTask;
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.CallbackEvent;
import com.topjohnwu.magisk.utils.Logger;
@@ -68,7 +69,7 @@ public class ReposFragment extends Fragment implements CallbackEvent.Listener<Vo
mSwipeRefreshLayout.setOnRefreshListener(() -> {
recyclerView.setVisibility(View.GONE);
new Async.LoadRepos(getApplication()).exec();
new LoadRepos(getActivity()).exec();
});
if (getApplication().repoLoadDone.isTriggered) {
@@ -132,10 +133,11 @@ public class ReposFragment extends Fragment implements CallbackEvent.Listener<Vo
for (Repo repo : getApplication().repoMap.values()) {
Module module = getApplication().moduleMap.get(repo.getId());
if (module != null) {
if (repo.getVersionCode() > module.getVersionCode())
if (repo.getVersionCode() > module.getVersionCode()) {
mUpdateRepos.add(repo);
else
} else {
mInstalledRepos.add(repo);
}
} else {
mOthersRepos.add(repo);
}
@@ -171,7 +173,7 @@ public class ReposFragment extends Fragment implements CallbackEvent.Listener<Vo
mSwipeRefreshLayout.setRefreshing(false);
}
private class FilterApps extends Async.NormalTask<String, Void, Void> {
private class FilterApps extends ParallelTask<String, Void, Void> {
@Override
protected Void doInBackground(String... strings) {
String newText = strings[0];

View File

@@ -13,9 +13,10 @@ import android.support.v7.widget.Toolbar;
import android.view.WindowManager;
import android.widget.Toast;
import com.topjohnwu.magisk.asyncs.MagiskHide;
import com.topjohnwu.magisk.asyncs.SerialTask;
import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.module.ModuleHelper;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
@@ -30,8 +31,8 @@ public class SettingsActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getTopApplication().isDarkTheme) {
setTheme(R.style.AppTheme_dh);
if (getApplicationContext().isDarkTheme) {
setTheme(R.style.AppTheme_Dark);
}
setContentView(R.layout.activity_container);
@@ -78,7 +79,7 @@ public class SettingsActivity extends Activity {
private ListPreference suAccess, autoRes, suNotification, requestTimeout;
private MagiskManager getApplication() {
return (MagiskManager) getActivity().getApplication();
return Utils.getMagiskManager(getActivity());
}
@Override
@@ -88,10 +89,6 @@ public class SettingsActivity extends Activity {
prefs = PreferenceManager.getDefaultSharedPreferences(getActivity());
prefScreen = getPreferenceScreen();
SwitchPreference busybox = (SwitchPreference) findPreference("busybox");
SwitchPreference magiskHide = (SwitchPreference) findPreference("magiskhide");
SwitchPreference hosts = (SwitchPreference) findPreference("hosts");
PreferenceCategory magiskCategory = (PreferenceCategory) findPreference("magisk");
PreferenceCategory suCategory = (PreferenceCategory) findPreference("superuser");
@@ -103,7 +100,7 @@ public class SettingsActivity extends Activity {
setSummary();
findPreference("clear").setOnPreferenceClickListener((pref) -> {
ModuleHelper.clearRepoCache(getApplication());
Utils.clearRepoCache(getActivity());
return true;
});
@@ -111,14 +108,11 @@ public class SettingsActivity extends Activity {
prefScreen.removePreference(magiskCategory);
prefScreen.removePreference(suCategory);
} else {
if (!getApplication().isSuClient)
if (!getApplication().isSuClient) {
prefScreen.removePreference(suCategory);
if (getApplication().magiskVersion < 11)
}
if (getApplication().magiskVersion < 11) {
prefScreen.removePreference(magiskCategory);
if (getApplication().disabled) {
busybox.setEnabled(false);
magiskHide.setEnabled(false);
hosts.setEnabled(false);
}
}
}
@@ -145,13 +139,13 @@ public class SettingsActivity extends Activity {
enabled = prefs.getBoolean("dark_theme", false);
if (getApplication().isDarkTheme != enabled) {
getApplication().isDarkTheme = enabled;
getActivity().recreate();
getApplication().reloadMainActivity.trigger();
getActivity().recreate();
}
break;
case "disable":
enabled = prefs.getBoolean("disable", false);
new Async.RootTask<Void, Void, Void>() {
new SerialTask<Void, Void, Void>() {
private boolean enable = enabled;
@Override
protected Void doInBackground(Void... voids) {
@@ -167,7 +161,7 @@ public class SettingsActivity extends Activity {
break;
case "busybox":
enabled = prefs.getBoolean("busybox", false);
new Async.RootTask<Void, Void, Void>() {
new SerialTask<Void, Void, Void>() {
private boolean enable = enabled;
@Override
protected Void doInBackground(Void... voids) {
@@ -188,19 +182,22 @@ public class SettingsActivity extends Activity {
enabled = prefs.getBoolean("magiskhide", false);
if (enabled) {
if (!getApplication().isSuClient) {
Utils.getAlertDialogBuilder(getActivity())
new AlertDialogBuilder(getActivity())
.setTitle(R.string.no_magisksu_title)
.setMessage(R.string.no_magisksu_msg)
.setPositiveButton(R.string.understand, (dialog, which) -> new Async.MagiskHide().enable())
.setPositiveButton(R.string.understand, (dialog, which) -> new MagiskHide().enable())
.setCancelable(false)
.show();
} else new Async.MagiskHide().enable();
} else
new Async.MagiskHide().disable();
} else {
new MagiskHide().enable();
}
} else {
new MagiskHide().disable();
}
break;
case "hosts":
enabled = prefs.getBoolean("hosts", false);
new Async.RootTask<Void, Void, Void>() {
new SerialTask<Void, Void, Void>() {
private boolean enable = enabled;
@Override
protected Void doInBackground(Void... voids) {
@@ -229,10 +226,10 @@ public class SettingsActivity extends Activity {
getApplication().suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1);
break;
case "developer_logging":
getApplication().devLogging = prefs.getBoolean("developer_logging", false);
MagiskManager.devLogging = prefs.getBoolean("developer_logging", false);
break;
case "shell_logging":
getApplication().shellLogging = prefs.getBoolean("shell_logging", false);
MagiskManager.shellLogging = prefs.getBoolean("shell_logging", false);
break;
}
setSummary();

View File

@@ -1,50 +1,73 @@
package com.topjohnwu.magisk;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.asyncs.GetBootBlocks;
import com.topjohnwu.magisk.asyncs.LoadApps;
import com.topjohnwu.magisk.asyncs.LoadModules;
import com.topjohnwu.magisk.asyncs.LoadRepos;
import com.topjohnwu.magisk.asyncs.MagiskHide;
import com.topjohnwu.magisk.components.Activity;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.services.UpdateCheckService;
import java.util.List;
public class SplashActivity extends Activity{
public class SplashActivity extends Activity {
private static final int UPDATE_SERVICE_ID = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MagiskManager magiskManager = getTopApplication();
MagiskManager magiskManager = getApplicationContext();
// 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;
// Initialize the update check service, notify every 3 hours
if (!"install".equals(getIntent().getStringExtra(MagiskManager.INTENT_SECTION))) {
ComponentName service = new ComponentName(magiskManager, UpdateCheckService.class);
JobInfo jobInfo = new JobInfo.Builder(UPDATE_SERVICE_ID, service)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setPersisted(true)
.setPeriodic(3 * 60 * 60 * 1000)
.build();
JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
scheduler.schedule(jobInfo);
}
// 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();
new GetBootBlocks(this).exec();
if (magiskManager.magiskHide && magiskManager.magiskVersion > 11 &&
!magiskManager.magiskHideStarted) {
new MagiskHide().enable();
}
new Async.LoadModules(magiskManager) {
new LoadModules(this) {
@Override
protected void onPostExecute(Void v) {
super.onPostExecute(v);
new Async.LoadRepos(magiskManager).exec();
new LoadRepos(activity).exec();
}
}.exec();
new LoadApps(this).exec();
new CheckUpdates(this, false){
@Override
protected void onPostExecute(Void v) {
super.onPostExecute(v);
String section = getIntent().getStringExtra(MagiskManager.INTENT_SECTION);
Intent intent = new Intent(magiskManager, MainActivity.class);
if (section != null) {
intent.putExtra(MagiskManager.INTENT_SECTION, section);
}
startActivity(intent);
finish();
}
}.exec();
new Async.LoadApps(magiskManager).exec();
// Preparation done, now start main activity
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
finish();
}
}

View File

@@ -1,11 +1,7 @@
package com.topjohnwu.magisk;
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;
@@ -14,8 +10,9 @@ import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.topjohnwu.magisk.asyncs.CheckUpdates;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.CallbackEvent;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell;
@@ -24,6 +21,7 @@ import com.topjohnwu.magisk.utils.Utils;
import butterknife.BindColor;
import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;
public class StatusFragment extends Fragment implements CallbackEvent.Listener<Void> {
@@ -55,9 +53,24 @@ public class StatusFragment extends Fragment implements CallbackEvent.Listener<V
@BindColor(R.color.grey500) int colorNeutral;
@BindColor(R.color.blue500) int colorInfo;
@BindColor(android.R.color.transparent) int trans;
int defaultColor;
private AlertDialog updateMagisk;
@OnClick(R.id.safetyNet_container)
public void safetyNet() {
safetyNetProgress.setVisibility(View.VISIBLE);
safetyNetContainer.setBackgroundColor(trans);
safetyNetIcon.setImageResource(0);
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
Utils.checkSafetyNet(getApplication());
}
@OnClick(R.id.magisk_status_container)
public void gotoInstall() {
if (getApplication().remoteMagiskVersion > 0) {
((MainActivity) getActivity()).navigate(R.id.install);
}
}
private int defaultColor;
@Nullable
@Override
@@ -84,31 +97,16 @@ public class StatusFragment extends Fragment implements CallbackEvent.Listener<V
noDialog = false;
updateUI();
new Async.CheckUpdates(getApplication()).exec();
});
safetyNetContainer.setOnClickListener(view -> {
safetyNetProgress.setVisibility(View.VISIBLE);
safetyNetContainer.setBackgroundColor(trans);
safetyNetIcon.setImageResource(0);
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
Async.checkSafetyNet(getApplication());
new CheckUpdates(getActivity()).exec();
});
if (getApplication().magiskVersion < 0 && Shell.rootAccess() && !noDialog) {
noDialog = true;
Utils.getAlertDialogBuilder(getActivity())
new AlertDialogBuilder(getActivity())
.setTitle(R.string.no_magisk_title)
.setMessage(R.string.no_magisk_msg)
.setCancelable(true)
.setPositiveButton(R.string.goto_install, (dialogInterface, i) -> {
((MainActivity) getActivity()).navigationView.setCheckedItem(R.id.install);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
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) {}
})
.setPositiveButton(R.string.goto_install, (d, i) -> gotoInstall())
.setNegativeButton(R.string.no_thanks, null)
.show();
}
@@ -163,7 +161,7 @@ public class StatusFragment extends Fragment implements CallbackEvent.Listener<V
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));
magiskVersionText.setText(getString(R.string.magisk_version_core_only, getApplication().magiskVersionString));
} else {
magiskVersionText.setText(getString(R.string.magisk_version, getApplication().magiskVersionString));
}
@@ -216,9 +214,6 @@ public class StatusFragment extends Fragment implements CallbackEvent.Listener<V
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);
@@ -228,32 +223,6 @@ public class StatusFragment extends Fragment implements CallbackEvent.Listener<V
magiskCheckUpdatesProgress.setVisibility(View.GONE);
mSwipeRefreshLayout.setRefreshing(false);
updateMagisk = Utils.getAlertDialogBuilder(getActivity())
.setTitle(R.string.magisk_update_title)
.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.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(getApplication().releaseNoteLink)));
})
.setNegativeButton(R.string.no_thanks, null)
.create();
if (getApplication().magiskVersion < getApplication().remoteMagiskVersion && Shell.rootAccess()) {
magiskStatusContainer.setOnClickListener(view -> updateMagisk.show());
if (!noDialog) {
noDialog = true;
updateMagisk.show();
}
}
}
private void updateSafetyNetUI() {

View File

@@ -13,7 +13,7 @@ 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.database.SuLogDatabaseHelper;
import com.topjohnwu.magisk.superuser.SuLogEntry;
import java.util.List;

View File

@@ -11,8 +11,8 @@ import android.widget.TextView;
import com.topjohnwu.magisk.adapters.PolicyAdapter;
import com.topjohnwu.magisk.components.Fragment;
import com.topjohnwu.magisk.database.SuDatabaseHelper;
import com.topjohnwu.magisk.superuser.Policy;
import com.topjohnwu.magisk.superuser.SuDatabaseHelper;
import java.util.List;

View File

@@ -13,7 +13,8 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.asyncs.MagiskHide;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.utils.Utils;
import java.util.ArrayList;
@@ -50,8 +51,8 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
}
public void setLists(List<ApplicationInfo> listApps, List<String> hideList) {
mOriginalList = mList = Collections.unmodifiableList(listApps);
mHideList = new ArrayList<>(hideList);
mOriginalList = mList = listApps;
mHideList = hideList;
notifyDataSetChanged();
}
@@ -76,20 +77,19 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
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();
});
holder.itemView.setOnClickListener(v ->
SnackbarMaker.make(holder.itemView,
R.string.safetyNet_hide_notice, Snackbar.LENGTH_LONG).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);
new MagiskHide().add(info.packageName);
mHideList.add(info.packageName);
} else {
new Async.MagiskHide().rm(info.packageName);
new MagiskHide().rm(info.packageName);
mHideList.remove(info.packageName);
}
});

View File

@@ -12,8 +12,9 @@ import android.widget.ImageView;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.SerialTask;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.module.Module;
import com.topjohnwu.magisk.utils.Async;
import com.topjohnwu.magisk.utils.Shell;
import java.util.List;
@@ -52,7 +53,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
holder.checkBox.setOnCheckedChangeListener(null);
holder.checkBox.setChecked(module.isEnabled());
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> new Async.RootTask<Void, Void, Void>() {
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> new SerialTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... voids) {
if (isChecked) {
@@ -66,11 +67,11 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
@Override
protected void onPostExecute(Void v) {
int snack = isChecked ? R.string.disable_file_removed : R.string.disable_file_created;
Snackbar.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
}
}.exec());
holder.delete.setOnClickListener(v -> new Async.RootTask<Void, Void, Void>() {
holder.delete.setOnClickListener(v -> new SerialTask<Void, Void, Void>() {
private final boolean removed = module.willBeRemoved();
@Override
@@ -86,7 +87,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
@Override
protected void onPostExecute(Void v) {
int snack = removed ? R.string.remove_file_deleted : R.string.remove_file_created;
Snackbar.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
updateDeleteButton(holder, module);
}
}.exec());

View File

@@ -15,9 +15,10 @@ import android.widget.Switch;
import android.widget.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.SnackbarMaker;
import com.topjohnwu.magisk.database.SuDatabaseHelper;
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;
@@ -70,7 +71,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
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();
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.addPolicy(policy);
}
});
@@ -80,7 +81,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
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();
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.addPolicy(policy);
}
});
@@ -90,18 +91,18 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
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();
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
dbHelper.addPolicy(policy);
}
});
holder.delete.setOnClickListener(v -> Utils.getAlertDialogBuilder(v.getContext())
holder.delete.setOnClickListener(v -> new AlertDialogBuilder(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),
SnackbarMaker.make(holder.itemView, v.getContext().getString(R.string.su_snack_revoke, policy.appName),
Snackbar.LENGTH_SHORT).show();
dbHelper.deletePolicy(policy.uid);
})

View File

@@ -1,30 +1,25 @@
package com.topjohnwu.magisk.adapters;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
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.TextView;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.asyncs.ProcessRepoZip;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.components.MarkDownWindow;
import com.topjohnwu.magisk.module.Repo;
import com.topjohnwu.magisk.receivers.RepoDlReceiver;
import com.topjohnwu.magisk.receivers.DownloadReceiver;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebWindow;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import butterknife.BindView;
import butterknife.ButterKnife;
@@ -32,7 +27,7 @@ import butterknife.ButterKnife;
public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder> {
private List<Repo> mUpdateRepos, mInstalledRepos, mOthersRepos;
private Set<Repo> expandList = new HashSet<>();
private Context mContext;
public ReposAdapter(List<Repo> update, List<Repo> installed, List<Repo> others) {
mUpdateRepos = update;
@@ -42,61 +37,52 @@ 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);
mContext = parent.getContext();
View v = LayoutInflater.from(mContext).inflate(R.layout.list_item_repo, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
Context context = holder.itemView.getContext();
Repo repo = getItem(position);
holder.title.setText(repo.getName());
holder.versionName.setText(repo.getVersion());
String author = repo.getAuthor();
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
holder.author.setText(TextUtils.isEmpty(author) ? null : mContext.getString(R.string.author, author));
holder.description.setText(repo.getDescription());
holder.setExpanded(expandList.contains(repo));
holder.infoLayout.setOnClickListener(v -> new MarkDownWindow(null, repo.getDetailUrl(), mContext));
holder.itemView.setOnClickListener(view -> {
if (holder.mExpanded) {
holder.collapse();
expandList.remove(repo);
} else {
holder.expand();
expandList.add(repo);
}
});
holder.changeLog.setOnClickListener(view -> {
if (!TextUtils.isEmpty(repo.getLogUrl())) {
new WebWindow(context.getString(R.string.changelog), repo.getLogUrl(), context);
}
});
holder.updateImage.setOnClickListener(view -> {
holder.downloadImage.setOnClickListener(v -> {
String filename = repo.getName() + "-" + repo.getVersion() + ".zip";
Utils.getAlertDialogBuilder(context)
.setTitle(context.getString(R.string.repo_install_title, repo.getName()))
.setMessage(context.getString(R.string.repo_install_msg, filename))
new AlertDialogBuilder(mContext)
.setTitle(mContext.getString(R.string.repo_install_title, repo.getName()))
.setMessage(mContext.getString(R.string.repo_install_msg, filename))
.setCancelable(true)
.setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive(
context,
new RepoDlReceiver(),
.setPositiveButton(R.string.install, (d, i) -> Utils.dlAndReceive(
mContext,
new DownloadReceiver() {
@Override
public void onDownloadDone(Uri uri) {
new ProcessRepoZip(activity, uri, true).exec();
}
},
repo.getZipUrl(),
Utils.getLegalFilename(filename)))
.setNeutralButton(R.string.download, (d, i) -> Utils.dlAndReceive(
mContext,
new DownloadReceiver() {
@Override
public void onDownloadDone(Uri uri) {
new ProcessRepoZip(activity, uri, false).exec();
}
},
repo.getZipUrl(),
Utils.getLegalFilename(filename)))
.setNegativeButton(R.string.no_thanks, null)
.show();
});
holder.authorLink.setOnClickListener(view -> {
if (!TextUtils.isEmpty(repo.getDonateUrl())) {
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(repo.getDonateUrl())));
}
});
holder.supportLink.setOnClickListener(view -> {
if (!TextUtils.isEmpty(repo.getSupportUrl())) {
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(repo.getSupportUrl())));
}
});
}
@Override
@@ -124,97 +110,12 @@ public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder>
@BindView(R.id.version_name) TextView versionName;
@BindView(R.id.description) TextView description;
@BindView(R.id.author) TextView author;
@BindView(R.id.expand_layout) LinearLayout expandLayout;
@BindView(R.id.update) ImageView updateImage;
@BindView(R.id.changeLog) ImageView changeLog;
@BindView(R.id.authorLink) ImageView authorLink;
@BindView(R.id.supportLink) ImageView supportLink;
private ValueAnimator mAnimator;
private ObjectAnimator animY2;
private boolean mExpanded = false;
private static int expandHeight = 0;
@BindView(R.id.info_layout) LinearLayout infoLayout;
@BindView(R.id.download) ImageView downloadImage;
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);
animY2 = ObjectAnimator.ofFloat(updateImage, "translationY", expandHeight / 2);
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);
if (expanded) {
updateImage.setTranslationY(expandHeight / 2);
} else {
updateImage.setTranslationY(0);
}
}
private void expand() {
expandLayout.setVisibility(View.VISIBLE);
mAnimator.start();
animY2.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();
animY2.reverse();
mExpanded = false;
}
private ValueAnimator slideAnimator(int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(valueAnimator -> {
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
layoutParams.height = value;
expandLayout.setLayoutParams(layoutParams);
});
return animator;
}
}

View File

@@ -109,7 +109,9 @@ public class SuLogAdapter {
@Override
public void onBindGroupViewHolder(LogGroupViewHolder holder, int flatPosition, ExpandableGroup group) {
holder.date.setText(group.getTitle());
if (isGroupExpanded(flatPosition)) holder.expand();
if (isGroupExpanded(flatPosition)) {
holder.expand();
}
}
}

View File

@@ -0,0 +1,72 @@
package com.topjohnwu.magisk.asyncs;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.TaskStackBuilder;
import android.support.v7.app.NotificationCompat;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.SplashActivity;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.WebService;
import org.json.JSONException;
import org.json.JSONObject;
public class CheckUpdates extends ParallelTask<Void, Void, Void> {
private static final String UPDATE_JSON = "https://raw.githubusercontent.com/topjohnwu/MagiskManager/updates/magisk_update.json";
private static final int NOTIFICATION_ID = 1;
private boolean showNotification = false;
public CheckUpdates(Context context, boolean b) {
this(context);
showNotification = b;
}
public CheckUpdates(Context context) {
magiskManager = Utils.getMagiskManager(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");
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) {
if (magiskManager.magiskVersion < magiskManager.remoteMagiskVersion
&& showNotification && magiskManager.updateNotification) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(magiskManager);
builder.setSmallIcon(R.drawable.ic_magisk)
.setContentTitle(magiskManager.getString(R.string.magisk_update_title))
.setContentText(magiskManager.getString(R.string.magisk_update_available, magiskManager.remoteMagiskVersion))
.setVibrate(new long[]{0, 100, 100, 100})
.setAutoCancel(true);
Intent intent = new Intent(magiskManager, SplashActivity.class);
intent.putExtra(MagiskManager.INTENT_SECTION, "install");
TaskStackBuilder stackBuilder = TaskStackBuilder.create(magiskManager);
stackBuilder.addParentStack(SplashActivity.class);
stackBuilder.addNextIntent(intent);
PendingIntent pendingIntent = stackBuilder.getPendingIntent(NOTIFICATION_ID, PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) magiskManager.getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID, builder.build());
}
magiskManager.updateCheckDone.trigger();
}
}

View File

@@ -0,0 +1,154 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.app.ProgressDialog;
import android.net.Uri;
import android.widget.Toast;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.R;
import com.topjohnwu.magisk.components.AlertDialogBuilder;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ZipUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
public class FlashZip extends SerialTask<Void, String, Integer> {
private Uri mUri;
private File mCachedFile, mScriptFile, mCheckFile;
private String mFilename;
private ProgressDialog progress;
public FlashZip(Activity context, Uri uri) {
super(context);
mUri = uri;
mCachedFile = new File(magiskManager.getCacheDir(), "install.zip");
mScriptFile = new File(magiskManager.getCacheDir(), "/META-INF/com/google/android/update-binary");
mCheckFile = new File(mScriptFile.getParent(), "updater-script");
// Try to get the filename ourselves
mFilename = Utils.getNameFromUri(magiskManager, mUri);
}
private void copyToCache() throws Throwable {
publishProgress(magiskManager.getString(R.string.copying_msg));
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);
Logger.dev("FlashZip: File created successfully - " + mCachedFile.getPath());
} catch (FileNotFoundException e) {
Logger.error("FlashZip: Invalid Uri");
throw e;
} catch (IOException e) {
Logger.error("FlashZip: Error in creating file");
throw e;
}
}
protected boolean unzipAndCheck() throws Exception {
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android");
List<String> ret;
ret = Utils.readFile(mCheckFile.getPath());
return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK");
}
private int cleanup(int ret) {
Shell.su(
"rm -rf " + mCachedFile.getParent() + "/*",
"rm -rf " + MagiskManager.TMP_FOLDER_PATH
);
return ret;
}
@Override
protected void onPreExecute() {
progress = new ProgressDialog(activity);
progress.setTitle(R.string.zip_install_progress_title);
progress.show();
}
@Override
protected void onProgressUpdate(String... values) {
progress.setMessage(values[0]);
}
@Override
protected Integer doInBackground(Void... voids) {
Logger.dev("FlashZip Running... " + mFilename);
List<String> ret;
try {
copyToCache();
if (!unzipAndCheck()) return cleanup(0);
publishProgress(magiskManager.getString(R.string.zip_install_progress_msg, mFilename));
ret = Shell.su(
"BOOTMODE=true sh " + mScriptFile + " dummy 1 " + mCachedFile,
"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);
}
if (Boolean.parseBoolean(ret.get(ret.size() - 1)))
return cleanup(1);
} catch (Throwable e) {
e.printStackTrace();
}
return cleanup(-1);
}
// -1 = error, manual install; 0 = invalid zip; 1 = success
@Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
progress.dismiss();
switch (result) {
case -1:
Toast.makeText(magiskManager, magiskManager.getString(R.string.install_error), Toast.LENGTH_LONG).show();
Utils.showUriSnack(activity, mUri);
break;
case 0:
Toast.makeText(magiskManager, magiskManager.getString(R.string.invalid_zip), Toast.LENGTH_LONG).show();
break;
case 1:
onSuccess();
break;
}
}
protected void onSuccess() {
magiskManager.updateCheckDone.trigger();
new LoadModules(activity).exec();
new AlertDialogBuilder(activity)
.setTitle(R.string.reboot_title)
.setMessage(R.string.reboot_msg)
.setPositiveButton(R.string.reboot, (dialogInterface, i) -> Shell.su(true, "reboot"))
.setNegativeButton(R.string.no_thanks, null)
.show();
}
}

View File

@@ -0,0 +1,27 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import com.topjohnwu.magisk.utils.Shell;
import com.topjohnwu.magisk.utils.Utils;
public class GetBootBlocks extends SerialTask<Void, Void, Void> {
public GetBootBlocks(Activity context) {
super(context);
}
@Override
protected Void doInBackground(Void... params) {
magiskManager.blockList = Shell.su("ls /dev/block | grep mmc");
if (magiskManager.bootBlock == null) {
magiskManager.bootBlock = Utils.detectBootImage();
}
return null;
}
@Override
protected void onPostExecute(Void v) {
magiskManager.blockDetectionDone.trigger();
}
}

View File

@@ -0,0 +1,39 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
public class LoadApps extends ParallelTask<Void, Void, Void> {
public LoadApps(Activity context) {
super(context);
}
@Override
protected Void doInBackground(Void... voids) {
PackageManager pm = magiskManager.getPackageManager();
List<ApplicationInfo> list = pm.getInstalledApplications(0);
for (Iterator<ApplicationInfo> i = list.iterator(); i.hasNext(); ) {
ApplicationInfo info = i.next();
if (ApplicationAdapter.BLACKLIST.contains(info.packageName) || !info.enabled) {
i.remove();
}
}
Collections.sort(list, (a, b) -> a.loadLabel(pm).toString().toLowerCase()
.compareTo(b.loadLabel(pm).toString().toLowerCase()));
magiskManager.appList = Collections.unmodifiableList(list);
return null;
}
@Override
protected void onPostExecute(Void v) {
new MagiskHide(activity).list();
}
}

View File

@@ -0,0 +1,41 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.module.BaseModule;
import com.topjohnwu.magisk.module.Module;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.Utils;
import com.topjohnwu.magisk.utils.ValueSortedMap;
public class LoadModules extends SerialTask<Void, Void, Void> {
public LoadModules(Activity context) {
super(context);
}
@Override
protected Void doInBackground(Void... voids) {
Logger.dev("LoadModules: Loading modules");
magiskManager.moduleMap = new ValueSortedMap<>();
for (String path : Utils.getModList(MagiskManager.MAGISK_PATH)) {
Logger.dev("LoadModules: Adding modules from " + path);
Module module;
try {
module = new Module(path);
magiskManager.moduleMap.put(module.getId(), module);
} catch (BaseModule.CacheModException ignored) {}
}
Logger.dev("LoadModules: Data load done");
return null;
}
@Override
protected void onPostExecute(Void v) {
magiskManager.moduleLoadDone.trigger();
}
}

View File

@@ -0,0 +1,125 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.content.SharedPreferences;
import android.text.TextUtils;
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
import com.topjohnwu.magisk.module.BaseModule;
import com.topjohnwu.magisk.module.Repo;
import com.topjohnwu.magisk.utils.Logger;
import com.topjohnwu.magisk.utils.ValueSortedMap;
import com.topjohnwu.magisk.utils.WebService;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public class LoadRepos extends ParallelTask<Void, Void, Void> {
public static final String ETAG_KEY = "ETag";
private static final String REPO_URL = "https://api.github.com/orgs/Magisk-Modules-Repo/repos";
private String prefsPath;
public LoadRepos(Activity context) {
super(context);
prefsPath = context.getApplicationInfo().dataDir + "/shared_prefs";
}
@Override
protected Void doInBackground(Void... voids) {
Logger.dev("LoadRepos: Loading repos");
SharedPreferences prefs = magiskManager.prefs;
RepoDatabaseHelper dbHelper = new RepoDatabaseHelper(magiskManager);
// Legacy data cleanup
File old = new File(prefsPath, "RepoMap.xml");
if (old.exists() || !prefs.getString("repomap", "empty").equals("empty")) {
old.delete();
prefs.edit().remove("version").remove("repomap").remove(ETAG_KEY).apply();
dbHelper.clearRepo();
}
Map<String, String> header = new HashMap<>();
// Get cached ETag to add in the request header
String etag = prefs.getString(ETAG_KEY, "");
header.put("If-None-Match", etag);
// Make a request to main URL for repo info
String jsonString = WebService.request(REPO_URL, WebService.GET, null, header, false);
ValueSortedMap<String, Repo> cached = dbHelper.getRepoMap(false), fetched = new ValueSortedMap<>();
if (!TextUtils.isEmpty(jsonString)) {
try {
JSONArray jsonArray = new JSONArray(jsonString);
// If it gets to this point, the response is valid, update ETag
etag = WebService.getLastResponseHeader().get(ETAG_KEY).get(0);
// Maybe bug in Android build tools, sometimes the ETag has crap in it...
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
// Update repo info
for (int i = 0; i < jsonArray.length(); i++) {
JSONObject jsonobject = jsonArray.getJSONObject(i);
String id = jsonobject.getString("description");
String name = jsonobject.getString("name");
String lastUpdate = jsonobject.getString("pushed_at");
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
Date updatedDate;
try {
updatedDate = format.parse(lastUpdate);
} catch (ParseException e) {
continue;
}
Repo repo = cached.get(id);
try {
if (repo == null) {
Logger.dev("LoadRepos: Create new repo " + id);
repo = new Repo(name, updatedDate);
} else {
// Popout from cached
cached.remove(id);
Logger.dev("LoadRepos: Update cached repo " + id);
repo.update(updatedDate);
}
if (repo.getId() != null) {
fetched.put(id, repo);
}
} catch (BaseModule.CacheModException ignored) {}
// Update the database
dbHelper.addRepoMap(fetched);
// The leftover cached are those removed remote, cleanup db
dbHelper.removeRepo(cached);
// Update ETag
prefs.edit().putString(ETAG_KEY, etag).apply();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
magiskManager.repoMap = dbHelper.getRepoMap();
Logger.dev("LoadRepos: Repo load done");
return null;
}
@Override
protected void onPostExecute(Void v) {
magiskManager.repoLoadDone.trigger();
}
}

View File

@@ -0,0 +1,59 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Shell;
import java.util.List;
public class MagiskHide extends SerialTask<Object, Void, Void> {
private boolean isList = false;
public MagiskHide() {}
public MagiskHide(Activity context) {
super(context);
}
@Override
protected Void doInBackground(Object... params) {
String command = (String) params[0];
List<String> ret = Shell.su(MagiskManager.MAGISK_HIDE_PATH + command);
if (isList) {
magiskManager.magiskHideList = ret;
}
return null;
}
@Override
protected void onPostExecute(Void v) {
if (isList) {
magiskManager.magiskHideDone.trigger();
}
}
public void add(CharSequence packageName) {
exec("add " + packageName);
}
public void rm(CharSequence 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 void list() {
isList = true;
if (magiskManager == null) return;
exec("list");
}
}

View File

@@ -0,0 +1,24 @@
package com.topjohnwu.magisk.asyncs;
import android.app.Activity;
import android.os.AsyncTask;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Utils;
public abstract class ParallelTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
protected Activity activity;
protected MagiskManager magiskManager;
public ParallelTask() {}
public ParallelTask(Activity context) {
activity = context;
magiskManager = Utils.getMagiskManager(context);
}
@SafeVarargs
public final void exec(Params... params) {
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
}
}

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