diff --git a/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt b/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt index 4a893ab8b..f166574b9 100644 --- a/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt +++ b/app/src/main/java/com/topjohnwu/magisk/extensions/XAndroid.kt @@ -177,7 +177,9 @@ fun Intent.toCommand(args: MutableList) { val sb = StringBuilder() val len = java.lang.reflect.Array.getLength(v) for (i in 0 until len) { - sb.append(java.lang.reflect.Array.get(v, i)!!.toString().replace(",", "\\,")) + sb.append( + java.lang.reflect.Array.get(v, i)!!.toString().replace(",", "\\,") + ) sb.append(',') } // Remove trailing comma @@ -262,10 +264,10 @@ fun Context.startEndToLeftRight(start: Int, end: Int): Pair { fun Context.openUrl(url: String) = Utils.openLink(this, url.toUri()) @Suppress("FunctionName") -inline fun T.DynamicClassLoader(apk: File) - = DynamicClassLoader(apk, T::class.java.classLoader) +inline fun T.DynamicClassLoader(apk: File) = + DynamicClassLoader(apk, T::class.java.classLoader) -fun Context.unwrap() : Context { +fun Context.unwrap(): Context { var context = this while (true) { if (context is ContextWrapper) @@ -275,3 +277,7 @@ fun Context.unwrap() : Context { } return context } + +fun Context.hasPermissions(vararg permissions: String) = permissions.all { + ContextCompat.checkSelfPermission(this, it) == PERMISSION_GRANTED +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/MagiskInstallDialog.kt b/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/MagiskInstallDialog.kt index 15ca84350..fff996862 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/MagiskInstallDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/events/dialog/MagiskInstallDialog.kt @@ -1,11 +1,19 @@ package com.topjohnwu.magisk.model.events.dialog +import android.Manifest +import android.net.Uri import android.view.LayoutInflater +import android.widget.Toast import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.R import com.topjohnwu.magisk.databinding.IncludeInstallOptionsBinding +import com.topjohnwu.magisk.extensions.hasPermissions import com.topjohnwu.magisk.extensions.res +import com.topjohnwu.magisk.model.download.DownloadService +import com.topjohnwu.magisk.model.entity.internal.Configuration +import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.events.OpenInappLinkEvent +import com.topjohnwu.magisk.utils.Utils import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MarkDownWindow import com.topjohnwu.superuser.Shell @@ -15,8 +23,10 @@ class MagiskInstallDialog : DialogEvent() { override fun build(dialog: MagiskDialog) { with(dialog) { - val filename = - "Magisk v${Info.remote.magisk.version}(${Info.remote.magisk.versionCode})" + val filename = "Magisk v%s (%d)".format( + Info.remote.magisk.version, + Info.remote.magisk.versionCode + ) applyTitle(R.string.repo_install_title.res(R.string.magisk.res())) applyMessage(R.string.repo_install_msg.res(filename)) setCancelable(true) @@ -24,9 +34,10 @@ class MagiskInstallDialog : DialogEvent() { titleRes = R.string.install preventDismiss = true onClick { - updateForInstallMethod(dialog) + updateForInstallMethod(dialog.reset()) } } + if (Info.remote.magisk.note.isEmpty()) return applyButton(MagiskDialog.ButtonType.NEGATIVE) { titleRes = R.string.release_notes @@ -44,38 +55,75 @@ class MagiskInstallDialog : DialogEvent() { private fun updateForInstallMethod(dialog: MagiskDialog) { with(dialog) { applyTitle(R.string.select_method) - applyMessage("") applyView(IncludeInstallOptionsBinding.inflate(LayoutInflater.from(dialog.context))) applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = R.string.download_zip_only onClick { preventDismiss = false - TODO() + download() } } applyButton(MagiskDialog.ButtonType.NEUTRAL) { + isEnabled = false titleRes = R.string.select_patch_file - onClick { - TODO() - } + // todo maybe leverage rxbus for this? + onClick { Utils.toast("This is not currently possible", Toast.LENGTH_LONG) } } + if (!Shell.rootAccess()) return applyButton(MagiskDialog.ButtonType.NEGATIVE) { titleRes = R.string.direct_install - onClick { - TODO() - } + onClick { flash() } } + if (!isABDevice()) return applyButton(MagiskDialog.ButtonType.IDGAF) { titleRes = R.string.install_inactive_slot - onClick { - TODO() - } + preventDismiss = true + onClick { inactiveSlotDialog(dialog.reset()) } } } } + private fun inactiveSlotDialog(dialog: MagiskDialog) { + dialog.applyTitle(R.string.warning) + .applyMessage(R.string.install_inactive_slot_msg) + .applyButton(MagiskDialog.ButtonType.POSITIVE) { + titleRes = R.string.yes + onClick { + flash(Configuration.Flash.Secondary) + } + } + .applyButton(MagiskDialog.ButtonType.NEGATIVE) { + titleRes = R.string.no_thanks + } + } + + // --- + + private fun hasPermissions() = dialog.context.hasPermissions( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ) + + private fun patch(data: Uri) = download(DownloadSubject.Magisk(Configuration.Patch(data))) + + private fun flash( + type: Configuration.Flash = Configuration.Flash.Primary + ) = download(DownloadSubject.Magisk(type)) + + private fun download( + type: DownloadSubject.Magisk = DownloadSubject.Magisk(Configuration.Download) + ) { + if (!hasPermissions()) { + Utils.toast("Storage permissions are required for this action", Toast.LENGTH_LONG) + return + } + DownloadService(dialog.context) { subject = type } + } + + // --- + private fun isABDevice() = ShellUtils .fastCmd("grep_prop ro.build.ab_update") .let { it.isNotEmpty() && it.toBoolean() } diff --git a/app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt b/app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt index 505eeeffc..105ec7c23 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt @@ -58,8 +58,20 @@ class MagiskDialog @JvmOverloads constructor( var preventDismiss = false fun clicked() { + //we might not want the click to dismiss the button to begin with + var prevention = preventDismiss + onClickAction(this@MagiskDialog) - if (!preventDismiss) { + + //in case we don't want the dialog to close after clicking the button + //ie. the input is incorrect ... + //otherwise we disregard the request, bcs it just might reset the button in the new + //instance + if (preventDismiss) { + prevention = preventDismiss + } + + if (!prevention) { dismiss() } } @@ -131,7 +143,7 @@ class MagiskDialog @JvmOverloads constructor( fun applyView(binding: Binding, body: Binding.() -> Unit = {}) = apply { - this.binding.dialogBaseContainer.removeAllViews() + resetView() this.binding.dialogBaseContainer.addView(binding.root) binding.apply(body) } @@ -144,6 +156,34 @@ class MagiskDialog @JvmOverloads constructor( fun reveal() = apply { super.show() } + // --- + + fun resetView() = apply { + binding.dialogBaseContainer.removeAllViews() + } + + fun resetTitle() = applyTitle("") + fun resetMessage() = applyMessage("") + fun resetIcon() = applyIcon(0) + + fun resetButtons() = apply { + ButtonType.values().forEach { + applyButton(it) { + title = "" + icon = 0 + isEnabled = true + preventDismiss = false + onClick {} + } + } + } + + fun reset() = resetTitle() + .resetMessage() + .resetView() + .resetIcon() + .resetButtons() + //region Deprecated Members @Deprecated("Use applyTitle instead", ReplaceWith("applyTitle")) override fun setTitle(title: CharSequence?) = Unit