Added most of the remaining functionality for Magisk install dialog

This commit is contained in:
Viktor De Pasquale 2019-10-21 19:22:16 +02:00
parent dc09ec7598
commit 8a014ff786
3 changed files with 114 additions and 20 deletions

View File

@ -177,7 +177,9 @@ fun Intent.toCommand(args: MutableList<String>) {
val sb = StringBuilder() val sb = StringBuilder()
val len = java.lang.reflect.Array.getLength(v) val len = java.lang.reflect.Array.getLength(v)
for (i in 0 until len) { 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(',') sb.append(',')
} }
// Remove trailing comma // Remove trailing comma
@ -262,10 +264,10 @@ fun Context.startEndToLeftRight(start: Int, end: Int): Pair<Int, Int> {
fun Context.openUrl(url: String) = Utils.openLink(this, url.toUri()) fun Context.openUrl(url: String) = Utils.openLink(this, url.toUri())
@Suppress("FunctionName") @Suppress("FunctionName")
inline fun <reified T> T.DynamicClassLoader(apk: File) inline fun <reified T> T.DynamicClassLoader(apk: File) =
= DynamicClassLoader(apk, T::class.java.classLoader) DynamicClassLoader(apk, T::class.java.classLoader)
fun Context.unwrap() : Context { fun Context.unwrap(): Context {
var context = this var context = this
while (true) { while (true) {
if (context is ContextWrapper) if (context is ContextWrapper)
@ -275,3 +277,7 @@ fun Context.unwrap() : Context {
} }
return context return context
} }
fun Context.hasPermissions(vararg permissions: String) = permissions.all {
ContextCompat.checkSelfPermission(this, it) == PERMISSION_GRANTED
}

View File

@ -1,11 +1,19 @@
package com.topjohnwu.magisk.model.events.dialog package com.topjohnwu.magisk.model.events.dialog
import android.Manifest
import android.net.Uri
import android.view.LayoutInflater import android.view.LayoutInflater
import android.widget.Toast
import com.topjohnwu.magisk.Info import com.topjohnwu.magisk.Info
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.IncludeInstallOptionsBinding import com.topjohnwu.magisk.databinding.IncludeInstallOptionsBinding
import com.topjohnwu.magisk.extensions.hasPermissions
import com.topjohnwu.magisk.extensions.res 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.model.events.OpenInappLinkEvent
import com.topjohnwu.magisk.utils.Utils
import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.magisk.view.MagiskDialog
import com.topjohnwu.magisk.view.MarkDownWindow import com.topjohnwu.magisk.view.MarkDownWindow
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
@ -15,8 +23,10 @@ class MagiskInstallDialog : DialogEvent() {
override fun build(dialog: MagiskDialog) { override fun build(dialog: MagiskDialog) {
with(dialog) { with(dialog) {
val filename = val filename = "Magisk v%s (%d)".format(
"Magisk v${Info.remote.magisk.version}(${Info.remote.magisk.versionCode})" Info.remote.magisk.version,
Info.remote.magisk.versionCode
)
applyTitle(R.string.repo_install_title.res(R.string.magisk.res())) applyTitle(R.string.repo_install_title.res(R.string.magisk.res()))
applyMessage(R.string.repo_install_msg.res(filename)) applyMessage(R.string.repo_install_msg.res(filename))
setCancelable(true) setCancelable(true)
@ -24,9 +34,10 @@ class MagiskInstallDialog : DialogEvent() {
titleRes = R.string.install titleRes = R.string.install
preventDismiss = true preventDismiss = true
onClick { onClick {
updateForInstallMethod(dialog) updateForInstallMethod(dialog.reset())
} }
} }
if (Info.remote.magisk.note.isEmpty()) return if (Info.remote.magisk.note.isEmpty()) return
applyButton(MagiskDialog.ButtonType.NEGATIVE) { applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = R.string.release_notes titleRes = R.string.release_notes
@ -44,38 +55,75 @@ class MagiskInstallDialog : DialogEvent() {
private fun updateForInstallMethod(dialog: MagiskDialog) { private fun updateForInstallMethod(dialog: MagiskDialog) {
with(dialog) { with(dialog) {
applyTitle(R.string.select_method) applyTitle(R.string.select_method)
applyMessage("")
applyView(IncludeInstallOptionsBinding.inflate(LayoutInflater.from(dialog.context))) applyView(IncludeInstallOptionsBinding.inflate(LayoutInflater.from(dialog.context)))
applyButton(MagiskDialog.ButtonType.POSITIVE) { applyButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = R.string.download_zip_only titleRes = R.string.download_zip_only
onClick { onClick {
preventDismiss = false preventDismiss = false
TODO() download()
} }
} }
applyButton(MagiskDialog.ButtonType.NEUTRAL) { applyButton(MagiskDialog.ButtonType.NEUTRAL) {
isEnabled = false
titleRes = R.string.select_patch_file titleRes = R.string.select_patch_file
onClick { // todo maybe leverage rxbus for this?
TODO() onClick { Utils.toast("This is not currently possible", Toast.LENGTH_LONG) }
}
} }
if (!Shell.rootAccess()) return if (!Shell.rootAccess()) return
applyButton(MagiskDialog.ButtonType.NEGATIVE) { applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = R.string.direct_install titleRes = R.string.direct_install
onClick { onClick { flash() }
TODO()
}
} }
if (!isABDevice()) return if (!isABDevice()) return
applyButton(MagiskDialog.ButtonType.IDGAF) { applyButton(MagiskDialog.ButtonType.IDGAF) {
titleRes = R.string.install_inactive_slot titleRes = R.string.install_inactive_slot
onClick { preventDismiss = true
TODO() 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 private fun isABDevice() = ShellUtils
.fastCmd("grep_prop ro.build.ab_update") .fastCmd("grep_prop ro.build.ab_update")
.let { it.isNotEmpty() && it.toBoolean() } .let { it.isNotEmpty() && it.toBoolean() }

View File

@ -58,8 +58,20 @@ class MagiskDialog @JvmOverloads constructor(
var preventDismiss = false var preventDismiss = false
fun clicked() { fun clicked() {
//we might not want the click to dismiss the button to begin with
var prevention = preventDismiss
onClickAction(this@MagiskDialog) 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() dismiss()
} }
} }
@ -131,7 +143,7 @@ class MagiskDialog @JvmOverloads constructor(
fun <Binding : ViewDataBinding> applyView(binding: Binding, body: Binding.() -> Unit = {}) = fun <Binding : ViewDataBinding> applyView(binding: Binding, body: Binding.() -> Unit = {}) =
apply { apply {
this.binding.dialogBaseContainer.removeAllViews() resetView()
this.binding.dialogBaseContainer.addView(binding.root) this.binding.dialogBaseContainer.addView(binding.root)
binding.apply(body) binding.apply(body)
} }
@ -144,6 +156,34 @@ class MagiskDialog @JvmOverloads constructor(
fun reveal() = apply { super.show() } 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 //region Deprecated Members
@Deprecated("Use applyTitle instead", ReplaceWith("applyTitle")) @Deprecated("Use applyTitle instead", ReplaceWith("applyTitle"))
override fun setTitle(title: CharSequence?) = Unit override fun setTitle(title: CharSequence?) = Unit