Merge remote-tracking branch 'john/master' into development
# Conflicts: # app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt
This commit is contained in:
commit
0beb08b687
@ -35,6 +35,16 @@ android {
|
|||||||
dataBinding {
|
dataBinding {
|
||||||
enabled = true
|
enabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
packagingOptions {
|
||||||
|
exclude '/META-INF/*.version'
|
||||||
|
exclude '/META-INF/*.kotlin_module'
|
||||||
|
exclude '/META-INF/rxkotlin.properties'
|
||||||
|
exclude '/androidsupportmultidexversion.txt'
|
||||||
|
exclude '/org/**'
|
||||||
|
exclude '/kotlin/**'
|
||||||
|
exclude '/kotlinx/**'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
@ -44,8 +54,6 @@ dependencies {
|
|||||||
implementation project(':signing')
|
implementation project(':signing')
|
||||||
|
|
||||||
implementation 'com.github.topjohnwu:jtar:1.0.0'
|
implementation 'com.github.topjohnwu:jtar:1.0.0'
|
||||||
implementation 'net.sourceforge.streamsupport:android-retrostreams:1.7.0'
|
|
||||||
implementation 'com.github.sevar83:indeterminate-checkbox:1.0.5'
|
|
||||||
implementation 'com.jakewharton.timber:timber:4.7.1'
|
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||||
implementation 'com.github.skoumalcz:teanity:0.3.3'
|
implementation 'com.github.skoumalcz:teanity:0.3.3'
|
||||||
implementation 'com.ncapdevi:frag-nav:3.2.0'
|
implementation 'com.ncapdevi:frag-nav:3.2.0'
|
||||||
|
@ -41,7 +41,8 @@ open class App : Application(), Application.ActivityLifecycleCallbacks {
|
|||||||
|
|
||||||
override fun attachBaseContext(base: Context) {
|
override fun attachBaseContext(base: Context) {
|
||||||
super.attachBaseContext(base)
|
super.attachBaseContext(base)
|
||||||
MultiDex.install(base)
|
if (BuildConfig.DEBUG)
|
||||||
|
MultiDex.install(base)
|
||||||
Timber.plant(Timber.DebugTree())
|
Timber.plant(Timber.DebugTree())
|
||||||
|
|
||||||
startKoin {
|
startKoin {
|
||||||
|
@ -1,85 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.tasks;
|
|
||||||
|
|
||||||
import android.net.Uri;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.App;
|
|
||||||
import com.topjohnwu.magisk.Const;
|
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
|
||||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
|
||||||
import com.topjohnwu.superuser.internal.UiThreadHandler;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
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 abstract class FlashZip {
|
|
||||||
|
|
||||||
private Uri mUri;
|
|
||||||
private File tmpFile;
|
|
||||||
private List<String> console, logs;
|
|
||||||
|
|
||||||
public FlashZip(Uri uri, List<String> out, List<String> err) {
|
|
||||||
mUri = uri;
|
|
||||||
console = out;
|
|
||||||
logs = err;
|
|
||||||
tmpFile = new File(App.self.getCacheDir(), "install.zip");
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean unzipAndCheck() throws IOException {
|
|
||||||
ZipUtils.unzip(tmpFile, tmpFile.getParentFile(), "META-INF/com/google/android", true);
|
|
||||||
return Shell.su("grep -q '#MAGISK' " + new File(tmpFile.getParentFile(), "updater-script"))
|
|
||||||
.exec().isSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean flash() throws IOException {
|
|
||||||
console.add("- Copying zip to temp directory");
|
|
||||||
try (InputStream in = App.self.getContentResolver().openInputStream(mUri);
|
|
||||||
OutputStream out = new BufferedOutputStream(new FileOutputStream(tmpFile))) {
|
|
||||||
if (in == null) throw new FileNotFoundException();
|
|
||||||
InputStream buf= new BufferedInputStream(in);
|
|
||||||
ShellUtils.pump(buf, out);
|
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
console.add("! Invalid Uri");
|
|
||||||
throw e;
|
|
||||||
} catch (IOException e) {
|
|
||||||
console.add("! Cannot copy to cache");
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
if (!unzipAndCheck()) {
|
|
||||||
console.add("! This zip is not a Magisk Module!");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
console.add("! Unzip error");
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
console.add("- Installing " + Utils.getNameFromUri(App.self, mUri));
|
|
||||||
return Shell.su("cd " + tmpFile.getParent(),
|
|
||||||
"BOOTMODE=true sh update-binary dummy 1 " + tmpFile)
|
|
||||||
.to(console, logs)
|
|
||||||
.exec().isSuccess();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void exec() {
|
|
||||||
App.THREAD_POOL.execute(() -> {
|
|
||||||
boolean success = false;
|
|
||||||
try {
|
|
||||||
success = flash();
|
|
||||||
} catch (IOException ignored) {}
|
|
||||||
Shell.su("cd /", "rm -rf " + tmpFile.getParent() + " " + Const.TMP_FOLDER_PATH).submit();
|
|
||||||
boolean finalSuccess = success;
|
|
||||||
UiThreadHandler.run(() -> onResult(finalSuccess));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void onResult(boolean success);
|
|
||||||
}
|
|
73
app/src/main/java/com/topjohnwu/magisk/tasks/FlashZip.kt
Normal file
73
app/src/main/java/com/topjohnwu/magisk/tasks/FlashZip.kt
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
package com.topjohnwu.magisk.tasks
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
|
||||||
|
import com.topjohnwu.magisk.App
|
||||||
|
import com.topjohnwu.magisk.Const
|
||||||
|
import com.topjohnwu.magisk.utils.*
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import com.topjohnwu.superuser.internal.UiThreadHandler
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
abstract class FlashZip(private val mUri: Uri,
|
||||||
|
private val console: MutableList<String>,
|
||||||
|
private val logs: MutableList<String>) {
|
||||||
|
|
||||||
|
private val tmpFile: File = File(App.self.cacheDir, "install.zip")
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
private fun unzipAndCheck(): Boolean {
|
||||||
|
unzip(tmpFile, tmpFile.parentFile!!, "META-INF/com/google/android", true)
|
||||||
|
return Shell.su("grep -q '#MAGISK' ${File(tmpFile.parentFile, "updater-script")}")
|
||||||
|
.exec().isSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
private fun flash(): Boolean {
|
||||||
|
console.add("- Copying zip to temp directory")
|
||||||
|
try {
|
||||||
|
App.self.readUri(mUri).use { input ->
|
||||||
|
tmpFile.outputStream().use { out -> input.copyTo(out) }
|
||||||
|
}
|
||||||
|
} catch (e: FileNotFoundException) {
|
||||||
|
console.add("! Invalid Uri")
|
||||||
|
throw e
|
||||||
|
} catch (e: IOException) {
|
||||||
|
console.add("! Cannot copy to cache")
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (!unzipAndCheck()) {
|
||||||
|
console.add("! This zip is not a Magisk Module!")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
console.add("! Unzip error")
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
console.add("- Installing ${mUri.fileName}")
|
||||||
|
return Shell.su("cd " + tmpFile.parent!!,
|
||||||
|
"BOOTMODE=true sh update-binary dummy 1 $tmpFile")
|
||||||
|
.to(console, logs)
|
||||||
|
.exec().isSuccess
|
||||||
|
}
|
||||||
|
|
||||||
|
fun exec() {
|
||||||
|
App.THREAD_POOL.execute {
|
||||||
|
val success = try {
|
||||||
|
flash()
|
||||||
|
} catch (e: IOException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
Shell.su("cd /", "rm -rf ${tmpFile.parent} ${Const.TMP_FOLDER_PATH}").submit()
|
||||||
|
UiThreadHandler.run { onResult(success) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract fun onResult(success: Boolean)
|
||||||
|
}
|
@ -24,6 +24,8 @@ import com.topjohnwu.magisk.utils.*
|
|||||||
import com.topjohnwu.superuser.Shell
|
import com.topjohnwu.superuser.Shell
|
||||||
import me.tatarka.bindingcollectionadapter2.ItemBinding
|
import me.tatarka.bindingcollectionadapter2.ItemBinding
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.Collections
|
||||||
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
class FlashViewModel(
|
class FlashViewModel(
|
||||||
action: String,
|
action: String,
|
||||||
@ -42,30 +44,36 @@ class FlashViewModel(
|
|||||||
itemBinding.bindExtra(BR.viewModel, this@FlashViewModel)
|
itemBinding.bindExtra(BR.viewModel, this@FlashViewModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val rawItems = ObservableArrayList<String>()
|
private val outItems = object : ObservableArrayList<String>() {
|
||||||
private val logItems = ObservableArrayList<String>()
|
override fun add(element: String?): Boolean {
|
||||||
|
if (element != null)
|
||||||
|
logItems.add(element)
|
||||||
|
return super.add(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private val logItems = Collections.synchronizedList(ArrayList<String>())
|
||||||
|
|
||||||
init {
|
init {
|
||||||
rawItems.sendUpdatesTo(items) { it.map { ConsoleRvItem(it) } }
|
outItems.sendUpdatesTo(items) { it.map { ConsoleRvItem(it) } }
|
||||||
|
|
||||||
state = State.LOADING
|
state = State.LOADING
|
||||||
|
|
||||||
val uri = uri ?: Uri.EMPTY
|
val uri = uri ?: Uri.EMPTY
|
||||||
when (action) {
|
when (action) {
|
||||||
Const.Value.FLASH_ZIP -> Flashing
|
Const.Value.FLASH_ZIP -> Flashing
|
||||||
.Install(uri, rawItems, logItems, this)
|
.Install(uri, outItems, logItems, this)
|
||||||
.exec()
|
.exec()
|
||||||
Const.Value.UNINSTALL -> Flashing
|
Const.Value.UNINSTALL -> Flashing
|
||||||
.Uninstall(uri, rawItems, logItems, this)
|
.Uninstall(uri, outItems, logItems, this)
|
||||||
.exec()
|
.exec()
|
||||||
Const.Value.FLASH_MAGISK -> Patching
|
Const.Value.FLASH_MAGISK -> Patching
|
||||||
.Direct(rawItems, logItems, this)
|
.Direct(outItems, logItems, this)
|
||||||
.exec()
|
.exec()
|
||||||
Const.Value.FLASH_INACTIVE_SLOT -> Patching
|
Const.Value.FLASH_INACTIVE_SLOT -> Patching
|
||||||
.SecondSlot(rawItems, logItems, this)
|
.SecondSlot(outItems, logItems, this)
|
||||||
.exec()
|
.exec()
|
||||||
Const.Value.PATCH_FILE -> Patching
|
Const.Value.PATCH_FILE -> Patching
|
||||||
.File(uri, rawItems, logItems, this)
|
.File(uri, outItems, logItems, this)
|
||||||
.exec()
|
.exec()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -90,16 +98,12 @@ class FlashViewModel(
|
|||||||
.map { Const.MAGISK_INSTALL_LOG_FILENAME.format(it) }
|
.map { Const.MAGISK_INSTALL_LOG_FILENAME.format(it) }
|
||||||
.map { File(Const.EXTERNAL_PATH, it) }
|
.map { File(Const.EXTERNAL_PATH, it) }
|
||||||
.map { file ->
|
.map { file ->
|
||||||
val log = items.filterIsInstance<ConsoleRvItem>()
|
file.bufferedWriter().use { writer ->
|
||||||
.joinToString("\n") { it.item }
|
logItems.forEach {
|
||||||
file.writeText(log)
|
writer.write(it)
|
||||||
|
writer.newLine()
|
||||||
val rawLog = logItems.toList().joinToString("\n")
|
}
|
||||||
if (rawLog.isNotBlank()) {
|
|
||||||
file.appendText("\n=== LOG ===\n")
|
|
||||||
file.appendText(rawLog)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
file.path
|
file.path
|
||||||
}
|
}
|
||||||
.subscribeK { SnackbarEvent(it).publish() }
|
.subscribeK { SnackbarEvent(it).publish() }
|
||||||
|
@ -89,7 +89,7 @@ public class DownloadApp {
|
|||||||
// Make it world readable
|
// Make it world readable
|
||||||
apk.setReadable(true, false);
|
apk.setReadable(true, false);
|
||||||
if (Shell.su("pm install " + apk).exec().isSuccess())
|
if (Shell.su("pm install " + apk).exec().isSuccess())
|
||||||
RootUtils.rmAndLaunch(app.getPackageName(),
|
RootUtils.Companion.rmAndLaunch(app.getPackageName(),
|
||||||
new ComponentName(BuildConfig.APPLICATION_ID,
|
new ComponentName(BuildConfig.APPLICATION_ID,
|
||||||
ClassMap.get(SplashActivity.class).getName()));
|
ClassMap.get(SplashActivity.class).getName()));
|
||||||
progress.dismiss();
|
progress.dismiss();
|
||||||
|
@ -3,6 +3,8 @@ package com.topjohnwu.magisk.utils;
|
|||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.core.app.NotificationCompat;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.App;
|
import com.topjohnwu.magisk.App;
|
||||||
import com.topjohnwu.magisk.BuildConfig;
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
import com.topjohnwu.magisk.ClassMap;
|
import com.topjohnwu.magisk.ClassMap;
|
||||||
@ -27,8 +29,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.jar.JarEntry;
|
import java.util.jar.JarEntry;
|
||||||
|
|
||||||
import androidx.core.app.NotificationCompat;
|
|
||||||
|
|
||||||
public class PatchAPK {
|
public class PatchAPK {
|
||||||
|
|
||||||
public static final String LOWERALPHA = "abcdefghijklmnopqrstuvwxyz";
|
public static final String LOWERALPHA = "abcdefghijklmnopqrstuvwxyz";
|
||||||
@ -112,7 +112,7 @@ public class PatchAPK {
|
|||||||
|
|
||||||
Config.set(Config.Key.SU_MANAGER, pkg);
|
Config.set(Config.Key.SU_MANAGER, pkg);
|
||||||
Config.export();
|
Config.export();
|
||||||
RootUtils.rmAndLaunch(BuildConfig.APPLICATION_ID,
|
RootUtils.Companion.rmAndLaunch(BuildConfig.APPLICATION_ID,
|
||||||
new ComponentName(pkg, ClassMap.get(SplashActivity.class).getName()));
|
new ComponentName(pkg, ClassMap.get(SplashActivity.class).getName()));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1,149 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.topjohnwu.magisk.Config;
|
|
||||||
import com.topjohnwu.magisk.Const;
|
|
||||||
import com.topjohnwu.magisk.R;
|
|
||||||
import com.topjohnwu.superuser.Shell;
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class RootUtils extends Shell.Initializer {
|
|
||||||
|
|
||||||
public static void rmAndLaunch(String rm, ComponentName component) {
|
|
||||||
Shell.su(Utils.fmt("(rm_launch %s %s)&", rm, component.flattenToString())).exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void reboot() {
|
|
||||||
Shell.su("/system/bin/reboot" + (Config.recovery ? " recovery" : "")).submit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startActivity(Intent intent) {
|
|
||||||
if (intent.getComponent() == null)
|
|
||||||
return;
|
|
||||||
List<String> args = new ArrayList<>();
|
|
||||||
args.add("am");
|
|
||||||
args.add("start");
|
|
||||||
intentToCommand(intent, args);
|
|
||||||
Shell.su(Utils.argsToCommand(args)).exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void intentToCommand(Intent intent, List<String> args) {
|
|
||||||
if (intent.getAction() != null) {
|
|
||||||
args.add("-a");
|
|
||||||
args.add(intent.getAction());
|
|
||||||
}
|
|
||||||
if (intent.getComponent() != null) {
|
|
||||||
args.add("-n");
|
|
||||||
args.add(intent.getComponent().flattenToString());
|
|
||||||
}
|
|
||||||
if (intent.getData() != null) {
|
|
||||||
args.add("-d");
|
|
||||||
args.add(intent.getDataString());
|
|
||||||
}
|
|
||||||
if (intent.getCategories() != null) {
|
|
||||||
for (String cat : intent.getCategories()) {
|
|
||||||
args.add("-c");
|
|
||||||
args.add(cat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (intent.getType() != null) {
|
|
||||||
args.add("-t");
|
|
||||||
args.add(intent.getType());
|
|
||||||
}
|
|
||||||
Bundle extras = intent.getExtras();
|
|
||||||
if (extras != null) {
|
|
||||||
for (String key : extras.keySet()) {
|
|
||||||
Object val = extras.get(key);
|
|
||||||
if (val == null)
|
|
||||||
continue;
|
|
||||||
Object value = val;
|
|
||||||
String arg;
|
|
||||||
if (val instanceof String) arg = "--es";
|
|
||||||
else if (val instanceof Boolean) arg = "--ez";
|
|
||||||
else if (val instanceof Integer) arg = "--ei";
|
|
||||||
else if (val instanceof Long) arg = "--el";
|
|
||||||
else if (val instanceof Float) arg = "--ef";
|
|
||||||
else if (val instanceof Uri) arg = "--eu";
|
|
||||||
else if (val instanceof ComponentName) {
|
|
||||||
arg = "--ecn";
|
|
||||||
value = ((ComponentName) val).flattenToString();
|
|
||||||
} else if (val instanceof ArrayList) {
|
|
||||||
ArrayList arr = (ArrayList) val;
|
|
||||||
if (arr.size() <= 0)
|
|
||||||
/* Impossible to know the type due to type erasure */
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (arr.get(0) instanceof Integer) arg = "--eial";
|
|
||||||
else if (arr.get(0) instanceof Long) arg = "--elal";
|
|
||||||
else if (arr.get(0) instanceof Float) arg = "--efal";
|
|
||||||
else if (arr.get(0) instanceof String) arg = "--esal";
|
|
||||||
else continue; /* Unsupported */
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (Object o : arr) {
|
|
||||||
sb.append(o.toString().replace(",", "\\,"));
|
|
||||||
sb.append(',');
|
|
||||||
}
|
|
||||||
// Remove trailing comma
|
|
||||||
sb.deleteCharAt(sb.length() - 1);
|
|
||||||
value = sb;
|
|
||||||
} else if (val.getClass().isArray()) {
|
|
||||||
if (val instanceof int[]) arg = "--eia";
|
|
||||||
else if (val instanceof long[]) arg = "--ela";
|
|
||||||
else if (val instanceof float[]) arg = "--efa";
|
|
||||||
else if (val instanceof String[]) arg = "--esa";
|
|
||||||
else continue; /* Unsupported */
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
int len = Array.getLength(val);
|
|
||||||
for (int i = 0; i < len; ++i) {
|
|
||||||
sb.append(Array.get(val, i).toString().replace(",", "\\,"));
|
|
||||||
sb.append(',');
|
|
||||||
}
|
|
||||||
// Remove trailing comma
|
|
||||||
sb.deleteCharAt(sb.length() - 1);
|
|
||||||
value = sb;
|
|
||||||
}
|
|
||||||
else continue; /* Unsupported */
|
|
||||||
|
|
||||||
args.add(arg);
|
|
||||||
args.add(key);
|
|
||||||
args.add(value.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
args.add("-f");
|
|
||||||
args.add(String.valueOf(intent.getFlags()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onInit(Context context, @NonNull Shell shell) {
|
|
||||||
Shell.Job job = shell.newJob();
|
|
||||||
if (shell.isRoot()) {
|
|
||||||
job.add(context.getResources().openRawResource(R.raw.util_functions))
|
|
||||||
.add(context.getResources().openRawResource(R.raw.utils));
|
|
||||||
Const.MAGISK_DISABLE_FILE = new SuFile("/cache/.disable_magisk");
|
|
||||||
Config.loadMagiskInfo();
|
|
||||||
} else {
|
|
||||||
InputStream nonroot = context.getResources().openRawResource(R.raw.nonroot_utils);
|
|
||||||
job.add(nonroot);
|
|
||||||
}
|
|
||||||
|
|
||||||
job.add("mount_partitions", "get_flags", "run_migrations", "export BOOTMODE=true").exec();
|
|
||||||
|
|
||||||
Config.keepVerity = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPVERITY"));
|
|
||||||
Config.keepEnc = Boolean.parseBoolean(ShellUtils.fastCmd("echo $KEEPFORCEENCRYPT"));
|
|
||||||
Config.recovery = Boolean.parseBoolean(ShellUtils.fastCmd("echo $RECOVERYMODE"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
164
app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt
Normal file
164
app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
package com.topjohnwu.magisk.utils
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import com.topjohnwu.magisk.Config
|
||||||
|
import com.topjohnwu.magisk.Const
|
||||||
|
import com.topjohnwu.magisk.R
|
||||||
|
import com.topjohnwu.superuser.Shell
|
||||||
|
import com.topjohnwu.superuser.ShellUtils
|
||||||
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
|
import java.util.*
|
||||||
|
import java.lang.reflect.Array as RArray
|
||||||
|
|
||||||
|
fun Intent.toCommand(args: MutableList<String>) {
|
||||||
|
if (action != null) {
|
||||||
|
args.add("-a")
|
||||||
|
args.add(action!!)
|
||||||
|
}
|
||||||
|
if (component != null) {
|
||||||
|
args.add("-n")
|
||||||
|
args.add(component!!.flattenToString())
|
||||||
|
}
|
||||||
|
if (data != null) {
|
||||||
|
args.add("-d")
|
||||||
|
args.add(dataString!!)
|
||||||
|
}
|
||||||
|
if (categories != null) {
|
||||||
|
for (cat in categories) {
|
||||||
|
args.add("-c")
|
||||||
|
args.add(cat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type != null) {
|
||||||
|
args.add("-t")
|
||||||
|
args.add(type!!)
|
||||||
|
}
|
||||||
|
val extras = extras
|
||||||
|
if (extras != null) {
|
||||||
|
for (key in extras.keySet()) {
|
||||||
|
val v = extras.get(key) ?: continue
|
||||||
|
var value: Any = v
|
||||||
|
val arg: String
|
||||||
|
if (v is String)
|
||||||
|
arg = "--es"
|
||||||
|
else if (v is Boolean)
|
||||||
|
arg = "--ez"
|
||||||
|
else if (v is Int)
|
||||||
|
arg = "--ei"
|
||||||
|
else if (v is Long)
|
||||||
|
arg = "--el"
|
||||||
|
else if (v is Float)
|
||||||
|
arg = "--ef"
|
||||||
|
else if (v is Uri)
|
||||||
|
arg = "--eu"
|
||||||
|
else if (v is ComponentName) {
|
||||||
|
arg = "--ecn"
|
||||||
|
value = v.flattenToString()
|
||||||
|
} else if (v is ArrayList<*>) {
|
||||||
|
if (v.size <= 0)
|
||||||
|
/* Impossible to know the type due to type erasure */
|
||||||
|
continue
|
||||||
|
|
||||||
|
arg = if (v[0] is Int)
|
||||||
|
"--eial"
|
||||||
|
else if (v[0] is Long)
|
||||||
|
"--elal"
|
||||||
|
else if (v[0] is Float)
|
||||||
|
"--efal"
|
||||||
|
else if (v[0] is String)
|
||||||
|
"--esal"
|
||||||
|
else
|
||||||
|
continue /* Unsupported */
|
||||||
|
|
||||||
|
val sb = StringBuilder()
|
||||||
|
for (o in v) {
|
||||||
|
sb.append(o.toString().replace(",", "\\,"))
|
||||||
|
sb.append(',')
|
||||||
|
}
|
||||||
|
// Remove trailing comma
|
||||||
|
sb.deleteCharAt(sb.length - 1)
|
||||||
|
value = sb
|
||||||
|
} else if (v.javaClass.isArray) {
|
||||||
|
arg = if (v is IntArray)
|
||||||
|
"--eia"
|
||||||
|
else if (v is LongArray)
|
||||||
|
"--ela"
|
||||||
|
else if (v is FloatArray)
|
||||||
|
"--efa"
|
||||||
|
else if (v is Array<*> && v.isArrayOf<String>())
|
||||||
|
"--esa"
|
||||||
|
else
|
||||||
|
continue /* Unsupported */
|
||||||
|
|
||||||
|
val sb = StringBuilder()
|
||||||
|
val len = RArray.getLength(v)
|
||||||
|
for (i in 0 until len) {
|
||||||
|
sb.append(RArray.get(v, i)!!.toString().replace(",", "\\,"))
|
||||||
|
sb.append(',')
|
||||||
|
}
|
||||||
|
// Remove trailing comma
|
||||||
|
sb.deleteCharAt(sb.length - 1)
|
||||||
|
value = sb
|
||||||
|
} else
|
||||||
|
continue /* Unsupported */
|
||||||
|
|
||||||
|
args.add(arg)
|
||||||
|
args.add(key)
|
||||||
|
args.add(value.toString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args.add("-f")
|
||||||
|
args.add(flags.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startActivity(intent: Intent) {
|
||||||
|
if (intent.component == null)
|
||||||
|
return
|
||||||
|
val args = ArrayList<String>()
|
||||||
|
args.add("am")
|
||||||
|
args.add("start")
|
||||||
|
intent.toCommand(args)
|
||||||
|
Shell.su(args.toShellCmd()).exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
class RootUtils : Shell.Initializer() {
|
||||||
|
|
||||||
|
override fun onInit(context: Context?, shell: Shell): Boolean {
|
||||||
|
context ?: return false
|
||||||
|
|
||||||
|
val job = shell.newJob()
|
||||||
|
if (shell.isRoot) {
|
||||||
|
job.add(context.rawResource(R.raw.util_functions))
|
||||||
|
.add(context.rawResource(R.raw.utils))
|
||||||
|
Const.MAGISK_DISABLE_FILE = SuFile("/cache/.disable_magisk")
|
||||||
|
Config.loadMagiskInfo()
|
||||||
|
} else {
|
||||||
|
job.add(context.rawResource(R.raw.nonroot_utils))
|
||||||
|
}
|
||||||
|
|
||||||
|
job.add("mount_partitions",
|
||||||
|
"get_flags",
|
||||||
|
"run_migrations",
|
||||||
|
"export BOOTMODE=true")
|
||||||
|
.exec()
|
||||||
|
|
||||||
|
Config.keepVerity = ShellUtils.fastCmd("echo \$KEEPVERITY").toBoolean()
|
||||||
|
Config.keepEnc = ShellUtils.fastCmd("echo \$KEEPFORCEENCRYPT").toBoolean()
|
||||||
|
Config.recovery = ShellUtils.fastCmd("echo \$RECOVERYMODE").toBoolean()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
fun rmAndLaunch(rm: String, component: ComponentName) {
|
||||||
|
Shell.su("(rm_launch $rm ${component.flattenToString()})").exec()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun reboot() {
|
||||||
|
Shell.su("/system/bin/reboot ${if (Config.recovery) "recovery" else ""}").submit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,6 +12,12 @@ import android.net.Uri;
|
|||||||
import android.provider.OpenableColumns;
|
import android.provider.OpenableColumns;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.work.Constraints;
|
||||||
|
import androidx.work.ExistingPeriodicWorkPolicy;
|
||||||
|
import androidx.work.NetworkType;
|
||||||
|
import androidx.work.PeriodicWorkRequest;
|
||||||
|
import androidx.work.WorkManager;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.App;
|
import com.topjohnwu.magisk.App;
|
||||||
import com.topjohnwu.magisk.BuildConfig;
|
import com.topjohnwu.magisk.BuildConfig;
|
||||||
import com.topjohnwu.magisk.ClassMap;
|
import com.topjohnwu.magisk.ClassMap;
|
||||||
@ -25,17 +31,10 @@ import com.topjohnwu.superuser.Shell;
|
|||||||
import com.topjohnwu.superuser.internal.UiThreadHandler;
|
import com.topjohnwu.superuser.internal.UiThreadHandler;
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import androidx.work.Constraints;
|
|
||||||
import androidx.work.ExistingPeriodicWorkPolicy;
|
|
||||||
import androidx.work.NetworkType;
|
|
||||||
import androidx.work.PeriodicWorkRequest;
|
|
||||||
import androidx.work.WorkManager;
|
|
||||||
|
|
||||||
public class Utils {
|
public class Utils {
|
||||||
|
|
||||||
public static void toast(CharSequence msg, int duration) {
|
public static void toast(CharSequence msg, int duration) {
|
||||||
@ -59,24 +58,6 @@ public class Utils {
|
|||||||
return getPrefsInt(prefs, key, 0);
|
return getPrefsInt(prefs, key, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getNameFromUri(Context context, Uri uri) {
|
|
||||||
String name = null;
|
|
||||||
try (Cursor c = context.getContentResolver().query(uri, null, null, null, null)) {
|
|
||||||
if (c != null) {
|
|
||||||
int nameIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
|
||||||
if (nameIndex != -1) {
|
|
||||||
c.moveToFirst();
|
|
||||||
name = c.getString(nameIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (name == null) {
|
|
||||||
int idx = uri.getPath().lastIndexOf('/');
|
|
||||||
name = uri.getPath().substring(idx + 1);
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int dpInPx(int dp) {
|
public static int dpInPx(int dp) {
|
||||||
float scale = App.self.getResources().getDisplayMetrics().density;
|
float scale = App.self.getResources().getDisplayMetrics().density;
|
||||||
return (int) (dp * scale + 0.5);
|
return (int) (dp * scale + 0.5);
|
||||||
@ -166,18 +147,4 @@ public class Utils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String argsToCommand(List<String> args) {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
for (String s : args) {
|
|
||||||
if (s.contains(" ")) {
|
|
||||||
sb.append('"').append(s).append('"');
|
|
||||||
} else {
|
|
||||||
sb.append(s);
|
|
||||||
}
|
|
||||||
sb.append(' ');
|
|
||||||
}
|
|
||||||
sb.deleteCharAt(sb.length() - 1);
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
package com.topjohnwu.magisk.utils
|
package com.topjohnwu.magisk.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import android.content.pm.ApplicationInfo
|
import android.content.pm.ApplicationInfo
|
||||||
import android.content.pm.ComponentInfo
|
import android.content.pm.ComponentInfo
|
||||||
import android.content.pm.PackageInfo
|
import android.content.pm.PackageInfo
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.content.pm.PackageManager.*
|
import android.content.pm.PackageManager.*
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.OpenableColumns
|
||||||
|
import com.topjohnwu.magisk.App
|
||||||
|
import java.io.FileNotFoundException
|
||||||
|
|
||||||
val PackageInfo.processes
|
val PackageInfo.processes
|
||||||
get() = activities?.processNames.orEmpty() +
|
get() = activities?.processNames.orEmpty() +
|
||||||
@ -37,6 +42,22 @@ val ApplicationInfo.packageInfo: PackageInfo?
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val Uri.fileName: String get() {
|
||||||
|
var name: String? = null
|
||||||
|
App.self.contentResolver.query(this, null, null, null, null)?.use { c ->
|
||||||
|
val nameIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME)
|
||||||
|
if (nameIndex != -1) {
|
||||||
|
c.moveToFirst()
|
||||||
|
name = c.getString(nameIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (name == null && path != null) {
|
||||||
|
val idx = path!!.lastIndexOf('/')
|
||||||
|
name = path!!.substring(idx + 1)
|
||||||
|
}
|
||||||
|
return name.orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
fun PackageManager.activities(packageName: String) =
|
fun PackageManager.activities(packageName: String) =
|
||||||
getPackageInfo(packageName, GET_ACTIVITIES)
|
getPackageInfo(packageName, GET_ACTIVITIES)
|
||||||
|
|
||||||
@ -48,3 +69,8 @@ fun PackageManager.receivers(packageName: String) =
|
|||||||
|
|
||||||
fun PackageManager.providers(packageName: String) =
|
fun PackageManager.providers(packageName: String) =
|
||||||
getPackageInfo(packageName, GET_PROVIDERS).providers
|
getPackageInfo(packageName, GET_PROVIDERS).providers
|
||||||
|
|
||||||
|
fun Context.rawResource(id: Int) = resources.openRawResource(id)
|
||||||
|
|
||||||
|
fun Context.readUri(uri: Uri) = contentResolver.openInputStream(uri) ?: throw FileNotFoundException()
|
||||||
|
|
||||||
|
@ -10,6 +10,20 @@ fun <T> MutableList<T>.update(newList: List<T>) {
|
|||||||
addAll(newList)
|
addAll(newList)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun List<String>.toShellCmd() : String {
|
||||||
|
val sb = StringBuilder()
|
||||||
|
for (s in this) {
|
||||||
|
if (s.contains(" ")) {
|
||||||
|
sb.append('"').append(s).append('"')
|
||||||
|
} else {
|
||||||
|
sb.append(s)
|
||||||
|
}
|
||||||
|
sb.append(' ')
|
||||||
|
}
|
||||||
|
sb.deleteCharAt(sb.length - 1)
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
fun <T1, T2> ObservableList<T1>.sendUpdatesTo(
|
fun <T1, T2> ObservableList<T1>.sendUpdatesTo(
|
||||||
target: DiffObservableList<T2>,
|
target: DiffObservableList<T2>,
|
||||||
mapper: (List<T1>) -> List<T2>
|
mapper: (List<T1>) -> List<T2>
|
||||||
|
@ -1,65 +0,0 @@
|
|||||||
package com.topjohnwu.magisk.utils;
|
|
||||||
|
|
||||||
import com.topjohnwu.signing.JarMap;
|
|
||||||
import com.topjohnwu.signing.SignAPK;
|
|
||||||
import com.topjohnwu.superuser.ShellUtils;
|
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
|
||||||
import com.topjohnwu.superuser.io.SuFileOutputStream;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.util.zip.ZipEntry;
|
|
||||||
import java.util.zip.ZipInputStream;
|
|
||||||
|
|
||||||
public class ZipUtils {
|
|
||||||
|
|
||||||
public static void unzip(File zip, File folder, String path, boolean junkPath) throws IOException {
|
|
||||||
InputStream in = new BufferedInputStream(new FileInputStream(zip));
|
|
||||||
unzip(in, folder, path, junkPath);
|
|
||||||
in.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void unzip(InputStream zip, File folder, String path, boolean junkPath) throws IOException {
|
|
||||||
try {
|
|
||||||
ZipInputStream zipfile = new ZipInputStream(zip);
|
|
||||||
ZipEntry entry;
|
|
||||||
while ((entry = zipfile.getNextEntry()) != null) {
|
|
||||||
if (!entry.getName().startsWith(path) || entry.isDirectory()){
|
|
||||||
// Ignore directories, only create files
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
String name;
|
|
||||||
if (junkPath) {
|
|
||||||
name = entry.getName().substring(entry.getName().lastIndexOf('/') + 1);
|
|
||||||
} else {
|
|
||||||
name = entry.getName();
|
|
||||||
}
|
|
||||||
File dest = new File(folder, name);
|
|
||||||
if (!dest.getParentFile().exists() && !dest.getParentFile().mkdirs()) {
|
|
||||||
dest = new SuFile(folder, name);
|
|
||||||
dest.getParentFile().mkdirs();
|
|
||||||
}
|
|
||||||
try (OutputStream out = new SuFileOutputStream(dest)) {
|
|
||||||
ShellUtils.pump(zipfile, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void signZip(File input, File output) throws Exception {
|
|
||||||
try (JarMap map = new JarMap(input, false);
|
|
||||||
BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(output))) {
|
|
||||||
SignAPK.sign(map, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
45
app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.kt
Normal file
45
app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.kt
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package com.topjohnwu.magisk.utils
|
||||||
|
|
||||||
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
|
import com.topjohnwu.superuser.io.SuFileOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.util.zip.ZipEntry
|
||||||
|
import java.util.zip.ZipInputStream
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun unzip(zip: File, folder: File, path: String = "", junkPath: Boolean = false) {
|
||||||
|
zip.inputStream().buffered().use {
|
||||||
|
unzip(it, folder, path, junkPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
fun unzip(zip: InputStream, folder: File, path: String, junkPath: Boolean) {
|
||||||
|
try {
|
||||||
|
val zin = ZipInputStream(zip)
|
||||||
|
var entry: ZipEntry
|
||||||
|
while (true) {
|
||||||
|
entry = zin.nextEntry ?: break
|
||||||
|
if (!entry.name.startsWith(path) || entry.isDirectory) {
|
||||||
|
// Ignore directories, only create files
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
val name = if (junkPath)
|
||||||
|
entry.name.substring(entry.name.lastIndexOf('/') + 1)
|
||||||
|
else
|
||||||
|
entry.name
|
||||||
|
|
||||||
|
var dest = File(folder, name)
|
||||||
|
if (!dest.parentFile!!.exists() && !dest.parentFile!!.mkdirs()) {
|
||||||
|
dest = SuFile(folder, name)
|
||||||
|
dest.parentFile!!.mkdirs()
|
||||||
|
}
|
||||||
|
SuFileOutputStream(dest).use { out -> zin.copyTo(out) }
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
@ -5,11 +5,11 @@ import android.net.Uri;
|
|||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import androidx.annotation.StringRes;
|
||||||
|
|
||||||
import com.google.android.material.snackbar.Snackbar;
|
import com.google.android.material.snackbar.Snackbar;
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.utils.Utils;
|
import com.topjohnwu.magisk.utils.XAndroidKt;
|
||||||
|
|
||||||
import androidx.annotation.StringRes;
|
|
||||||
|
|
||||||
public class SnackbarMaker {
|
public class SnackbarMaker {
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ public class SnackbarMaker {
|
|||||||
|
|
||||||
public static void showUri(Activity activity, Uri uri) {
|
public static void showUri(Activity activity, Uri uri) {
|
||||||
make(activity, activity.getString(R.string.internal_storage,
|
make(activity, activity.getString(R.string.internal_storage,
|
||||||
"/Download/" + Utils.getNameFromUri(activity, uri)),
|
"/Download/" + XAndroidKt.getFileName(uri)),
|
||||||
Snackbar.LENGTH_LONG)
|
Snackbar.LENGTH_LONG)
|
||||||
.setAction(R.string.ok, (v)->{}).show();
|
.setAction(R.string.ok, (v)->{}).show();
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@ import android.app.Activity;
|
|||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.topjohnwu.magisk.R;
|
import com.topjohnwu.magisk.R;
|
||||||
import com.topjohnwu.magisk.tasks.MagiskInstaller;
|
import com.topjohnwu.magisk.tasks.MagiskInstaller;
|
||||||
import com.topjohnwu.magisk.utils.RootUtils;
|
import com.topjohnwu.magisk.utils.RootUtils;
|
||||||
@ -12,8 +14,6 @@ import com.topjohnwu.superuser.Shell;
|
|||||||
import com.topjohnwu.superuser.internal.UiThreadHandler;
|
import com.topjohnwu.superuser.internal.UiThreadHandler;
|
||||||
import com.topjohnwu.superuser.io.SuFile;
|
import com.topjohnwu.superuser.io.SuFile;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
public class EnvFixDialog extends CustomAlertDialog {
|
public class EnvFixDialog extends CustomAlertDialog {
|
||||||
|
|
||||||
public EnvFixDialog(@NonNull Activity activity) {
|
public EnvFixDialog(@NonNull Activity activity) {
|
||||||
@ -38,7 +38,7 @@ public class EnvFixDialog extends CustomAlertDialog {
|
|||||||
pd.dismiss();
|
pd.dismiss();
|
||||||
Utils.toast(success ? R.string.reboot_delay_toast : R.string.setup_fail, Toast.LENGTH_LONG);
|
Utils.toast(success ? R.string.reboot_delay_toast : R.string.setup_fail, Toast.LENGTH_LONG);
|
||||||
if (success)
|
if (success)
|
||||||
UiThreadHandler.handler.postDelayed(RootUtils::reboot, 5000);
|
UiThreadHandler.handler.postDelayed(RootUtils.Companion::reboot, 5000);
|
||||||
}
|
}
|
||||||
}.exec();
|
}.exec();
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user