diff --git a/app/build.gradle b/app/build.gradle index fbdb42cf9..68be34143 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,6 +35,16 @@ android { dataBinding { 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 { @@ -44,8 +54,6 @@ dependencies { implementation project(':signing') 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.github.skoumalcz:teanity:0.3.3' implementation 'com.ncapdevi:frag-nav:3.2.0' diff --git a/app/src/main/java/com/topjohnwu/magisk/App.kt b/app/src/main/java/com/topjohnwu/magisk/App.kt index 4fcec157c..e34bfaacc 100644 --- a/app/src/main/java/com/topjohnwu/magisk/App.kt +++ b/app/src/main/java/com/topjohnwu/magisk/App.kt @@ -41,7 +41,8 @@ open class App : Application(), Application.ActivityLifecycleCallbacks { override fun attachBaseContext(base: Context) { super.attachBaseContext(base) - MultiDex.install(base) + if (BuildConfig.DEBUG) + MultiDex.install(base) Timber.plant(Timber.DebugTree()) startKoin { diff --git a/app/src/main/java/com/topjohnwu/magisk/tasks/FlashZip.java b/app/src/main/java/com/topjohnwu/magisk/tasks/FlashZip.java deleted file mode 100644 index 50803ec27..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/tasks/FlashZip.java +++ /dev/null @@ -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 console, logs; - - public FlashZip(Uri uri, List out, List 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); -} diff --git a/app/src/main/java/com/topjohnwu/magisk/tasks/FlashZip.kt b/app/src/main/java/com/topjohnwu/magisk/tasks/FlashZip.kt new file mode 100644 index 000000000..0594dc2bb --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/tasks/FlashZip.kt @@ -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, + private val logs: MutableList) { + + 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) +} diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt index 4dbc278c7..968faeab4 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/flash/FlashViewModel.kt @@ -24,6 +24,8 @@ import com.topjohnwu.magisk.utils.* import com.topjohnwu.superuser.Shell import me.tatarka.bindingcollectionadapter2.ItemBinding import java.io.File +import java.util.Collections +import kotlin.collections.ArrayList class FlashViewModel( action: String, @@ -42,30 +44,36 @@ class FlashViewModel( itemBinding.bindExtra(BR.viewModel, this@FlashViewModel) } - private val rawItems = ObservableArrayList() - private val logItems = ObservableArrayList() + private val outItems = object : ObservableArrayList() { + override fun add(element: String?): Boolean { + if (element != null) + logItems.add(element) + return super.add(element) + } + } + private val logItems = Collections.synchronizedList(ArrayList()) init { - rawItems.sendUpdatesTo(items) { it.map { ConsoleRvItem(it) } } + outItems.sendUpdatesTo(items) { it.map { ConsoleRvItem(it) } } state = State.LOADING val uri = uri ?: Uri.EMPTY when (action) { Const.Value.FLASH_ZIP -> Flashing - .Install(uri, rawItems, logItems, this) + .Install(uri, outItems, logItems, this) .exec() Const.Value.UNINSTALL -> Flashing - .Uninstall(uri, rawItems, logItems, this) + .Uninstall(uri, outItems, logItems, this) .exec() Const.Value.FLASH_MAGISK -> Patching - .Direct(rawItems, logItems, this) + .Direct(outItems, logItems, this) .exec() Const.Value.FLASH_INACTIVE_SLOT -> Patching - .SecondSlot(rawItems, logItems, this) + .SecondSlot(outItems, logItems, this) .exec() Const.Value.PATCH_FILE -> Patching - .File(uri, rawItems, logItems, this) + .File(uri, outItems, logItems, this) .exec() } } @@ -90,16 +98,12 @@ class FlashViewModel( .map { Const.MAGISK_INSTALL_LOG_FILENAME.format(it) } .map { File(Const.EXTERNAL_PATH, it) } .map { file -> - val log = items.filterIsInstance() - .joinToString("\n") { it.item } - file.writeText(log) - - val rawLog = logItems.toList().joinToString("\n") - if (rawLog.isNotBlank()) { - file.appendText("\n=== LOG ===\n") - file.appendText(rawLog) + file.bufferedWriter().use { writer -> + logItems.forEach { + writer.write(it) + writer.newLine() + } } - file.path } .subscribeK { SnackbarEvent(it).publish() } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/DownloadApp.java b/app/src/main/java/com/topjohnwu/magisk/utils/DownloadApp.java index 39727e52d..e10d38420 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/DownloadApp.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/DownloadApp.java @@ -89,7 +89,7 @@ public class DownloadApp { // Make it world readable apk.setReadable(true, false); if (Shell.su("pm install " + apk).exec().isSuccess()) - RootUtils.rmAndLaunch(app.getPackageName(), + RootUtils.Companion.rmAndLaunch(app.getPackageName(), new ComponentName(BuildConfig.APPLICATION_ID, ClassMap.get(SplashActivity.class).getName())); progress.dismiss(); diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java index 0d77c058a..95389302f 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/PatchAPK.java @@ -3,6 +3,8 @@ package com.topjohnwu.magisk.utils; import android.content.ComponentName; import android.widget.Toast; +import androidx.core.app.NotificationCompat; + import com.topjohnwu.magisk.App; import com.topjohnwu.magisk.BuildConfig; import com.topjohnwu.magisk.ClassMap; @@ -27,8 +29,6 @@ import java.util.ArrayList; import java.util.List; import java.util.jar.JarEntry; -import androidx.core.app.NotificationCompat; - public class PatchAPK { public static final String LOWERALPHA = "abcdefghijklmnopqrstuvwxyz"; @@ -112,7 +112,7 @@ public class PatchAPK { Config.set(Config.Key.SU_MANAGER, pkg); Config.export(); - RootUtils.rmAndLaunch(BuildConfig.APPLICATION_ID, + RootUtils.Companion.rmAndLaunch(BuildConfig.APPLICATION_ID, new ComponentName(pkg, ClassMap.get(SplashActivity.class).getName())); return true; diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.java b/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.java deleted file mode 100644 index 216c2a3d0..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.java +++ /dev/null @@ -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 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 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; - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt b/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt new file mode 100644 index 000000000..baa9ed7dd --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/RootUtils.kt @@ -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) { + 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()) + "--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() + 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() + } + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java index 1cbcb06d5..83c686696 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java +++ b/app/src/main/java/com/topjohnwu/magisk/utils/Utils.java @@ -12,6 +12,12 @@ import android.net.Uri; import android.provider.OpenableColumns; 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.BuildConfig; import com.topjohnwu.magisk.ClassMap; @@ -25,17 +31,10 @@ import com.topjohnwu.superuser.Shell; import com.topjohnwu.superuser.internal.UiThreadHandler; import com.topjohnwu.superuser.io.SuFile; -import java.util.List; import java.util.Locale; import java.util.Map; 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 static void toast(CharSequence msg, int duration) { @@ -59,24 +58,6 @@ public class Utils { 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) { float scale = App.self.getResources().getDisplayMetrics().density; return (int) (dp * scale + 0.5); @@ -166,18 +147,4 @@ public class Utils { } } - public static String argsToCommand(List 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(); - } - } diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt b/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt index bf594c755..0db67d30b 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/XAndroid.kt @@ -1,10 +1,15 @@ package com.topjohnwu.magisk.utils +import android.content.Context import android.content.pm.ApplicationInfo import android.content.pm.ComponentInfo import android.content.pm.PackageInfo 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 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) = getPackageInfo(packageName, GET_ACTIVITIES) @@ -47,4 +68,9 @@ fun PackageManager.receivers(packageName: String) = getPackageInfo(packageName, GET_RECEIVERS).receivers fun PackageManager.providers(packageName: String) = - getPackageInfo(packageName, GET_PROVIDERS).providers \ No newline at end of file + getPackageInfo(packageName, GET_PROVIDERS).providers + +fun Context.rawResource(id: Int) = resources.openRawResource(id) + +fun Context.readUri(uri: Uri) = contentResolver.openInputStream(uri) ?: throw FileNotFoundException() + diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/XList.kt b/app/src/main/java/com/topjohnwu/magisk/utils/XList.kt index 705ee9bb3..26b5009fa 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/XList.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/XList.kt @@ -10,6 +10,20 @@ fun MutableList.update(newList: List) { addAll(newList) } +fun List.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 ObservableList.sendUpdatesTo( target: DiffObservableList, mapper: (List) -> List diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.java b/app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.java deleted file mode 100644 index 42bfcbdd8..000000000 --- a/app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.java +++ /dev/null @@ -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); - } - } -} diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.kt b/app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.kt new file mode 100644 index 000000000..80a90a13a --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/ZipUtils.kt @@ -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 + } +} diff --git a/app/src/main/java/com/topjohnwu/magisk/view/SnackbarMaker.java b/app/src/main/java/com/topjohnwu/magisk/view/SnackbarMaker.java index dc0f1ca61..5884824e1 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/SnackbarMaker.java +++ b/app/src/main/java/com/topjohnwu/magisk/view/SnackbarMaker.java @@ -5,11 +5,11 @@ import android.net.Uri; import android.view.View; import android.widget.TextView; +import androidx.annotation.StringRes; + import com.google.android.material.snackbar.Snackbar; import com.topjohnwu.magisk.R; -import com.topjohnwu.magisk.utils.Utils; - -import androidx.annotation.StringRes; +import com.topjohnwu.magisk.utils.XAndroidKt; public class SnackbarMaker { @@ -41,7 +41,7 @@ public class SnackbarMaker { public static void showUri(Activity activity, Uri uri) { make(activity, activity.getString(R.string.internal_storage, - "/Download/" + Utils.getNameFromUri(activity, uri)), + "/Download/" + XAndroidKt.getFileName(uri)), Snackbar.LENGTH_LONG) .setAction(R.string.ok, (v)->{}).show(); } diff --git a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java index 95e1364b6..aff566fb3 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java +++ b/app/src/main/java/com/topjohnwu/magisk/view/dialogs/EnvFixDialog.java @@ -4,6 +4,8 @@ import android.app.Activity; import android.app.ProgressDialog; import android.widget.Toast; +import androidx.annotation.NonNull; + import com.topjohnwu.magisk.R; import com.topjohnwu.magisk.tasks.MagiskInstaller; 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.io.SuFile; -import androidx.annotation.NonNull; - public class EnvFixDialog extends CustomAlertDialog { public EnvFixDialog(@NonNull Activity activity) { @@ -38,7 +38,7 @@ public class EnvFixDialog extends CustomAlertDialog { pd.dismiss(); Utils.toast(success ? R.string.reboot_delay_toast : R.string.setup_fail, Toast.LENGTH_LONG); if (success) - UiThreadHandler.handler.postDelayed(RootUtils::reboot, 5000); + UiThreadHandler.handler.postDelayed(RootUtils.Companion::reboot, 5000); } }.exec(); });