Clean up MagiskDialog

This commit is contained in:
topjohnwu 2022-01-21 00:50:02 -08:00
parent 7c9908d953
commit 47545b45b8
15 changed files with 266 additions and 346 deletions

View File

@ -75,15 +75,16 @@ abstract class BaseMainActivity<VM : BaseViewModel, Binding : ViewDataBinding>
private fun showInvalidStateMessage() { private fun showInvalidStateMessage() {
runOnUiThread { runOnUiThread {
MagiskDialog(this) MagiskDialog(this).apply {
.applyTitle(R.string.unsupport_nonroot_stub_title) setTitle(R.string.unsupport_nonroot_stub_title)
.applyMessage(R.string.unsupport_nonroot_stub_msg) setMessage(R.string.unsupport_nonroot_stub_msg)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { setButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = R.string.install text = R.string.install
onClick { HideAPK.restore(this@BaseMainActivity) } onClick { HideAPK.restore(this@BaseMainActivity) }
} }
.cancellable(false) setCancelable(false)
.reveal() show()
}
} }
} }

View File

@ -1,8 +1,6 @@
package com.topjohnwu.magisk.events.dialog package com.topjohnwu.magisk.events.dialog
import android.app.Activity import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Config
@ -11,31 +9,25 @@ import com.topjohnwu.magisk.view.MagiskDialog
class DarkThemeDialog : DialogEvent() { class DarkThemeDialog : DialogEvent() {
override fun build(dialog: MagiskDialog) { override fun build(dialog: MagiskDialog) {
val activity = dialog.context.unwrap() val activity = dialog.ownerActivity!!
dialog.applyTitle(R.string.settings_dark_mode_title) dialog.apply {
.applyMessage(R.string.settings_dark_mode_message) setTitle(R.string.settings_dark_mode_title)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { setMessage(R.string.settings_dark_mode_message)
titleRes = R.string.settings_dark_mode_light setButton(MagiskDialog.ButtonType.POSITIVE) {
text = R.string.settings_dark_mode_light
icon = R.drawable.ic_day icon = R.drawable.ic_day
onClick { selectTheme(AppCompatDelegate.MODE_NIGHT_NO, activity) } onClick { selectTheme(AppCompatDelegate.MODE_NIGHT_NO, activity) }
} }
.applyButton(MagiskDialog.ButtonType.NEUTRAL) { setButton(MagiskDialog.ButtonType.NEUTRAL) {
titleRes = R.string.settings_dark_mode_system text = R.string.settings_dark_mode_system
icon = R.drawable.ic_day_night icon = R.drawable.ic_day_night
onClick { selectTheme(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM, activity) } onClick { selectTheme(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM, activity) }
} }
.applyButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = R.string.settings_dark_mode_dark text = R.string.settings_dark_mode_dark
icon = R.drawable.ic_night icon = R.drawable.ic_night
onClick { selectTheme(AppCompatDelegate.MODE_NIGHT_YES, activity) } onClick { selectTheme(AppCompatDelegate.MODE_NIGHT_YES, activity) }
} }
}
private fun Context.unwrap(): Activity {
return when(this) {
is Activity -> this
is ContextWrapper -> baseContext.unwrap()
else -> error("Cannot happen")
} }
} }

View File

@ -7,12 +7,10 @@ import com.topjohnwu.magisk.view.MagiskDialog
abstract class DialogEvent : ViewEvent(), ActivityExecutor { abstract class DialogEvent : ViewEvent(), ActivityExecutor {
protected lateinit var dialog: MagiskDialog
override fun invoke(activity: BaseUIActivity<*, *>) { override fun invoke(activity: BaseUIActivity<*, *>) {
dialog = MagiskDialog(activity) MagiskDialog(activity)
.apply { setOwnerActivity(activity) } .apply { setOwnerActivity(activity) }
.apply(this::build).reveal() .apply(this::build).show()
} }
abstract fun build(dialog: MagiskDialog) abstract fun build(dialog: MagiskDialog)

View File

@ -13,16 +13,19 @@ import kotlinx.coroutines.launch
class EnvFixDialog(private val vm: HomeViewModel) : DialogEvent() { class EnvFixDialog(private val vm: HomeViewModel) : DialogEvent() {
override fun build(dialog: MagiskDialog) { override fun build(dialog: MagiskDialog) {
dialog.applyTitle(R.string.env_fix_title) dialog.apply {
.applyMessage(R.string.env_fix_msg) setTitle(R.string.env_fix_title)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { setMessage(R.string.env_fix_msg)
titleRes = android.R.string.ok setButton(MagiskDialog.ButtonType.POSITIVE) {
preventDismiss = true text = android.R.string.ok
doNotDismiss = true
onClick { onClick {
dialog.applyTitle(R.string.setup_title) dialog.apply {
.applyMessage(R.string.setup_msg) setTitle(R.string.setup_title)
.resetButtons() setMessage(R.string.setup_msg)
.cancellable(false) resetButtons()
setCancelable(false)
}
(dialog.ownerActivity as BaseActivity).lifecycleScope.launch { (dialog.ownerActivity as BaseActivity).lifecycleScope.launch {
MagiskInstaller.FixEnv { MagiskInstaller.FixEnv {
dialog.dismiss() dialog.dismiss()
@ -30,14 +33,15 @@ class EnvFixDialog(private val vm: HomeViewModel) : DialogEvent() {
} }
} }
} }
.applyButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel text = android.R.string.cancel
} }
}
if (Info.env.versionCode != BuildConfig.VERSION_CODE || if (Info.env.versionCode != BuildConfig.VERSION_CODE ||
Info.env.versionString != BuildConfig.VERSION_NAME) { Info.env.versionString != BuildConfig.VERSION_NAME) {
dialog.applyButton(MagiskDialog.ButtonType.POSITIVE) { dialog.setButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = android.R.string.ok text = android.R.string.ok
onClick { onClick {
vm.onMagiskPressed() vm.onMagiskPressed()
dialog.dismiss() dialog.dismiss()

View File

@ -25,14 +25,14 @@ class ManagerInstallDialog : MarkDownDialog() {
override fun build(dialog: MagiskDialog) { override fun build(dialog: MagiskDialog) {
super.build(dialog) super.build(dialog)
with(dialog) { dialog.apply {
setCancelable(true) setCancelable(true)
applyButton(MagiskDialog.ButtonType.POSITIVE) { setButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = R.string.install text = R.string.install
onClick { DownloadService.start(context, Subject.Manager()) } onClick { DownloadService.start(context, Subject.Manager()) }
} }
applyButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel text = android.R.string.cancel
} }
} }
} }

View File

@ -22,7 +22,7 @@ abstract class MarkDownDialog : DialogEvent() {
override fun build(dialog: MagiskDialog) { override fun build(dialog: MagiskDialog) {
with(dialog) { with(dialog) {
val view = LayoutInflater.from(context).inflate(R.layout.markdown_window_md2, null) val view = LayoutInflater.from(context).inflate(R.layout.markdown_window_md2, null)
applyView(view) setView(view)
(ownerActivity as BaseActivity).lifecycleScope.launch { (ownerActivity as BaseActivity).lifecycleScope.launch {
val tv = view.findViewById<TextView>(R.id.md_txt) val tv = view.findViewById<TextView>(R.id.md_txt)
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {

View File

@ -11,7 +11,7 @@ import com.topjohnwu.magisk.view.MagiskDialog
class ModuleInstallDialog(private val item: OnlineModule) : DialogEvent() { class ModuleInstallDialog(private val item: OnlineModule) : DialogEvent() {
override fun build(dialog: MagiskDialog) { override fun build(dialog: MagiskDialog) {
with(dialog) { dialog.apply {
fun download(install: Boolean) { fun download(install: Boolean) {
val action = if (install) Action.Flash else Action.Download val action = if (install) Action.Flash else Action.Download
@ -19,24 +19,22 @@ class ModuleInstallDialog(private val item: OnlineModule) : DialogEvent() {
DownloadService.start(context, subject) DownloadService.start(context, subject)
} }
applyTitle(context.getString(R.string.repo_install_title, item.name)) setTitle(context.getString(R.string.repo_install_title, item.name))
.applyMessage(context.getString(R.string.repo_install_msg, item.downloadFilename)) setMessage(context.getString(R.string.repo_install_msg, item.downloadFilename))
.cancellable(true) setCancelable(true)
.applyButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = R.string.download text = R.string.download
icon = R.drawable.ic_download_md2 icon = R.drawable.ic_download_md2
onClick { download(false) } onClick { download(false) }
} }
if (Info.env.isActive) { if (Info.env.isActive) {
applyButton(MagiskDialog.ButtonType.POSITIVE) { setButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = R.string.install text = R.string.install
icon = R.drawable.ic_install icon = R.drawable.ic_install
onClick { download(true) } onClick { download(true) }
} }
} }
reveal()
} }
} }

View File

@ -6,11 +6,13 @@ import com.topjohnwu.magisk.view.MagiskDialog
class SecondSlotWarningDialog : DialogEvent() { class SecondSlotWarningDialog : DialogEvent() {
override fun build(dialog: MagiskDialog) { override fun build(dialog: MagiskDialog) {
dialog.applyTitle(android.R.string.dialog_alert_title) dialog.apply {
.applyMessage(R.string.install_inactive_slot_msg) setTitle(android.R.string.dialog_alert_title)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { setMessage(R.string.install_inactive_slot_msg)
titleRes = android.R.string.ok setButton(MagiskDialog.ButtonType.POSITIVE) {
text = android.R.string.ok
} }
.cancellable(true) setCancelable(true)
}
} }
} }

View File

@ -10,15 +10,17 @@ class SuperuserRevokeDialog(
private val callbacks = Builder().apply(builder) private val callbacks = Builder().apply(builder)
override fun build(dialog: MagiskDialog) { override fun build(dialog: MagiskDialog) {
dialog.applyTitle(R.string.su_revoke_title) dialog.apply {
.applyMessage(R.string.su_revoke_msg, callbacks.appName) setTitle(R.string.su_revoke_title)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { setMessage(R.string.su_revoke_msg, callbacks.appName)
titleRes = android.R.string.ok setButton(MagiskDialog.ButtonType.POSITIVE) {
text = android.R.string.ok
onClick { callbacks.listenerOnSuccess() } onClick { callbacks.listenerOnSuccess() }
} }
.applyButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel text = android.R.string.cancel
} }
}
} }
inner class Builder internal constructor() { inner class Builder internal constructor() {

View File

@ -1,6 +1,7 @@
package com.topjohnwu.magisk.events.dialog package com.topjohnwu.magisk.events.dialog
import android.app.ProgressDialog import android.app.ProgressDialog
import android.content.Context
import android.widget.Toast import android.widget.Toast
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.arch.BaseUIActivity import com.topjohnwu.magisk.arch.BaseUIActivity
@ -12,22 +13,24 @@ import com.topjohnwu.superuser.Shell
class UninstallDialog : DialogEvent() { class UninstallDialog : DialogEvent() {
override fun build(dialog: MagiskDialog) { override fun build(dialog: MagiskDialog) {
dialog.applyTitle(R.string.uninstall_magisk_title) dialog.apply {
.applyMessage(R.string.uninstall_magisk_msg) setTitle(R.string.uninstall_magisk_title)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { setMessage(R.string.uninstall_magisk_msg)
titleRes = R.string.restore_img setButton(MagiskDialog.ButtonType.POSITIVE) {
onClick { restore() } text = R.string.restore_img
onClick { restore(dialog.context) }
} }
.applyButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = R.string.complete_uninstall text = R.string.complete_uninstall
onClick { completeUninstall() } onClick { completeUninstall(dialog) }
} }
}
} }
@Suppress("DEPRECATION") @Suppress("DEPRECATION")
private fun restore() { private fun restore(context: Context) {
val dialog = ProgressDialog(dialog.context).apply { val dialog = ProgressDialog(context).apply {
setMessage(dialog.context.getString(R.string.restore_img_msg)) setMessage(context.getString(R.string.restore_img_msg))
show() show()
} }
@ -41,9 +44,9 @@ class UninstallDialog : DialogEvent() {
} }
} }
private fun completeUninstall() { private fun completeUninstall(dialog: MagiskDialog) {
(dialog.ownerActivity as? BaseUIActivity<*, *>) (dialog.ownerActivity as BaseUIActivity<*, *>)
?.navigation?.navigate(FlashFragment.uninstall()) .navigation?.navigate(FlashFragment.uninstall())
} }
} }

View File

@ -146,42 +146,42 @@ class MainActivity : BaseMainActivity<MainViewModel, ActivityMainMd2Binding>() {
private fun showUnsupportedMessage() { private fun showUnsupportedMessage() {
if (Info.env.isUnsupported) { if (Info.env.isUnsupported) {
MagiskDialog(this) MagiskDialog(this).apply {
.applyTitle(R.string.unsupport_magisk_title) setTitle(R.string.unsupport_magisk_title)
.applyMessage(R.string.unsupport_magisk_msg, Const.Version.MIN_VERSION) setMessage(R.string.unsupport_magisk_msg, Const.Version.MIN_VERSION)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok } setButton(MagiskDialog.ButtonType.POSITIVE) { text = android.R.string.ok }
.cancellable(false) setCancelable(false)
.reveal() }.show()
} }
if (!Info.isEmulator && Info.env.isActive && System.getenv("PATH") if (!Info.isEmulator && Info.env.isActive && System.getenv("PATH")
?.split(':') ?.split(':')
?.filterNot { File("$it/magisk").exists() } ?.filterNot { File("$it/magisk").exists() }
?.any { File("$it/su").exists() } == true) { ?.any { File("$it/su").exists() } == true) {
MagiskDialog(this) MagiskDialog(this).apply {
.applyTitle(R.string.unsupport_general_title) setTitle(R.string.unsupport_general_title)
.applyMessage(R.string.unsupport_other_su_msg) setMessage(R.string.unsupport_other_su_msg)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok } setButton(MagiskDialog.ButtonType.POSITIVE) { text = android.R.string.ok }
.cancellable(false) setCancelable(false)
.reveal() }.show()
} }
if (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0) { if (applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM != 0) {
MagiskDialog(this) MagiskDialog(this).apply {
.applyTitle(R.string.unsupport_general_title) setTitle(R.string.unsupport_general_title)
.applyMessage(R.string.unsupport_system_app_msg) setMessage(R.string.unsupport_system_app_msg)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok } setButton(MagiskDialog.ButtonType.POSITIVE) { text = android.R.string.ok }
.cancellable(false) setCancelable(false)
.reveal() }.show()
} }
if (applicationInfo.flags and ApplicationInfo.FLAG_EXTERNAL_STORAGE != 0) { if (applicationInfo.flags and ApplicationInfo.FLAG_EXTERNAL_STORAGE != 0) {
MagiskDialog(this) MagiskDialog(this).apply {
.applyTitle(R.string.unsupport_general_title) setTitle(R.string.unsupport_general_title)
.applyMessage(R.string.unsupport_external_storage_msg) setMessage(R.string.unsupport_external_storage_msg)
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok } setButton(MagiskDialog.ButtonType.POSITIVE) { text = android.R.string.ok }
.cancellable(false) setCancelable(false)
.reveal() }.show()
} }
} }
@ -190,18 +190,20 @@ class MainActivity : BaseMainActivity<MainViewModel, ActivityMainMd2Binding>() {
ShortcutManagerCompat.isRequestPinShortcutSupported(this)) { ShortcutManagerCompat.isRequestPinShortcutSupported(this)) {
// Ask and show dialog // Ask and show dialog
Config.askedHome = true Config.askedHome = true
MagiskDialog(this) MagiskDialog(this).apply {
.applyTitle(R.string.add_shortcut_title) setTitle(R.string.add_shortcut_title)
.applyMessage(R.string.add_shortcut_msg) setMessage(R.string.add_shortcut_msg)
.applyButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel text = android.R.string.cancel
}.applyButton(MagiskDialog.ButtonType.POSITIVE) { }
titleRes = android.R.string.ok setButton(MagiskDialog.ButtonType.POSITIVE) {
text = android.R.string.ok
onClick { onClick {
Shortcuts.addHomeIcon(this@MainActivity) Shortcuts.addHomeIcon(this@MainActivity)
} }
}.cancellable(true) }
.reveal() setCancelable(true)
}.show()
} }
} }
} }

View File

@ -110,25 +110,25 @@ sealed class BaseSettingsItem : ObservableRvItem() {
protected abstract val inputResult: String? protected abstract val inputResult: String?
override fun onPressed(view: View) { override fun onPressed(view: View) {
MagiskDialog(view.context) MagiskDialog(view.context).apply {
.applyTitle(title.getText(view.resources)) setTitle(title.getText(view.resources))
.applyView(getView(view.context)) setView(getView(view.context))
.applyButton(MagiskDialog.ButtonType.POSITIVE) { setButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = android.R.string.ok text = android.R.string.ok
onClick { onClick {
inputResult?.let { result -> inputResult?.let { result ->
preventDismiss = false doNotDismiss = false
value = result value = result
it.dismiss() it.dismiss()
return@onClick return@onClick
} }
preventDismiss = true doNotDismiss = true
} }
} }
.applyButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel text = android.R.string.cancel
} }
.reveal() }.show()
} }
abstract fun getView(context: Context): View abstract fun getView(context: Context): View
@ -151,15 +151,15 @@ sealed class BaseSettingsItem : ObservableRvItem() {
runCatching { getStringArray(id) }.getOrDefault(emptyArray()) runCatching { getStringArray(id) }.getOrDefault(emptyArray())
override fun onPressed(view: View) { override fun onPressed(view: View) {
MagiskDialog(view.context) MagiskDialog(view.context).apply {
.applyTitle(title.getText(view.resources)) setTitle(title.getText(view.resources))
.applyButton(MagiskDialog.ButtonType.NEGATIVE) { setButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel text = android.R.string.cancel
} }
.applyAdapter(entries(view.resources)) { setListItems(entries(view.resources)) {
value = it value = it
} }
.reveal() }.show()
} }
} }

View File

@ -15,7 +15,6 @@ import androidx.appcompat.app.AppCompatDialog
import androidx.appcompat.content.res.AppCompatResources import androidx.appcompat.content.res.AppCompatResources
import androidx.databinding.Bindable import androidx.databinding.Bindable
import androidx.databinding.PropertyChangeRegistry import androidx.databinding.PropertyChangeRegistry
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
@ -23,9 +22,12 @@ import com.google.android.material.shape.MaterialShapeDrawable
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.* import com.topjohnwu.magisk.databinding.*
import com.topjohnwu.magisk.view.MagiskDialog.DialogClickListener
import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapters import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapters
import me.tatarka.bindingcollectionadapter2.ItemBinding import me.tatarka.bindingcollectionadapter2.ItemBinding
typealias DialogButtonClickListener = (DialogInterface) -> Unit
class MagiskDialog( class MagiskDialog(
context: Context, theme: Int = 0 context: Context, theme: Int = 0
) : AppCompatDialog(context, theme) { ) : AppCompatDialog(context, theme) {
@ -39,6 +41,82 @@ class MagiskDialog(
setCancelable(true) setCancelable(true)
} }
inner class Data : ObservableHost {
override var callbacks: PropertyChangeRegistry? = null
@get:Bindable
var icon: Drawable? = null
set(value) = set(value, field, { field = it }, BR.icon)
@get:Bindable
var title: CharSequence = ""
set(value) = set(value, field, { field = it }, BR.title)
@get:Bindable
var message: CharSequence = ""
set(value) = set(value, field, { field = it }, BR.message)
val buttonPositive = ButtonViewModel()
val buttonNeutral = ButtonViewModel()
val buttonNegative = ButtonViewModel()
}
enum class ButtonType {
POSITIVE, NEUTRAL, NEGATIVE
}
interface Button {
var icon: Int
var text: Any
var isEnabled: Boolean
var doNotDismiss: Boolean
fun onClick(listener: DialogButtonClickListener)
}
inner class ButtonViewModel : Button, ObservableHost {
override var callbacks: PropertyChangeRegistry? = null
@get:Bindable
override var icon = 0
set(value) = set(value, field, { field = it }, BR.icon, BR.gone)
@get:Bindable
var message: String = ""
set(value) = set(value, field, { field = it }, BR.message, BR.gone)
override var text: Any
get() = message
set(value) {
message = when (value) {
is Int -> context.getText(value)
else -> value
}.toString()
}
@get:Bindable
val gone get() = icon == 0 && message.isEmpty()
@get:Bindable
override var isEnabled = true
set(value) = set(value, field, { field = it }, BR.enabled)
override var doNotDismiss = false
private var onClickAction: DialogButtonClickListener = {}
override fun onClick(listener: DialogButtonClickListener) {
onClickAction = listener
}
fun clicked() {
onClickAction(this@MagiskDialog)
if (!doNotDismiss) {
dismiss()
}
}
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
super.setContentView(binding.root) super.setContentView(binding.root)
@ -58,129 +136,29 @@ class MagiskDialog(
} }
} }
inner class Data : ObservableHost { override fun setTitle(@StringRes titleId: Int) { data.title = context.getString(titleId) }
override var callbacks: PropertyChangeRegistry? = null
@get:Bindable override fun setTitle(title: CharSequence?) { data.title = title ?: "" }
var icon: Drawable? = null
set(value) = set(value, field, { field = it }, BR.icon)
@get:Bindable fun setMessage(@StringRes msgId: Int, vararg args: Any) {
var title: CharSequence = "" data.message = context.getString(msgId, *args)
set(value) = set(value, field, { field = it }, BR.title)
@get:Bindable
var message: CharSequence = ""
set(value) = set(value, field, { field = it }, BR.message)
val buttonPositive = Button()
val buttonNeutral = Button()
val buttonNegative = Button()
val buttonIDGAF = Button()
} }
enum class ButtonType { fun setMessage(message: CharSequence) { data.message = message }
POSITIVE, NEUTRAL, NEGATIVE, IDGAF
fun setIcon(@DrawableRes drawableRes: Int) {
data.icon = AppCompatResources.getDrawable(context, drawableRes)
} }
inner class Button : ObservableHost { fun setIcon(drawable: Drawable) { data.icon = drawable }
override var callbacks: PropertyChangeRegistry? = null
@get:Bindable fun setButton(buttonType: ButtonType, builder: Button.() -> Unit) {
var icon = 0
set(value) = set(value, field, { field = it }, BR.icon)
@get:Bindable
var title: CharSequence = ""
set(value) = set(value, field, { field = it }, BR.title)
@get:Bindable
var isEnabled = true
set(value) = set(value, field, { field = it }, BR.enabled)
var onClickAction: OnDialogButtonClickListener = {}
var preventDismiss = false
fun clicked() {
//we might not want the click to dismiss the button to begin with
var prevention = preventDismiss
onClickAction(this@MagiskDialog)
//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()
}
}
}
inner class ButtonBuilder(private val button: Button) {
var icon: Int
get() = button.icon
set(value) {
button.icon = value
}
var title: CharSequence
get() = button.title
set(value) {
button.title = value
}
var titleRes: Int
get() = 0
set(value) {
button.title = context.getString(value)
}
var isEnabled: Boolean
get() = button.isEnabled
set(value) {
button.isEnabled = value
}
var preventDismiss: Boolean
get() = button.preventDismiss
set(value) {
button.preventDismiss = value
}
fun onClick(listener: OnDialogButtonClickListener) {
button.onClickAction = listener
}
}
fun applyTitle(@StringRes stringRes: Int) =
apply { data.title = context.getString(stringRes) }
fun applyTitle(title: CharSequence) =
apply { data.title = title }
fun applyMessage(@StringRes stringRes: Int, vararg args: Any) =
apply { data.message = context.getString(stringRes, *args) }
fun applyMessage(message: CharSequence) =
apply { data.message = message }
fun applyIcon(@DrawableRes drawableRes: Int) =
apply {
data.icon = AppCompatResources.getDrawable(context, drawableRes)
}
fun applyIcon(drawable: Drawable) =
apply { data.icon = drawable }
fun applyButton(buttonType: ButtonType, builder: ButtonBuilder.() -> Unit) = apply {
val button = when (buttonType) { val button = when (buttonType) {
ButtonType.POSITIVE -> data.buttonPositive ButtonType.POSITIVE -> data.buttonPositive
ButtonType.NEUTRAL -> data.buttonNeutral ButtonType.NEUTRAL -> data.buttonNeutral
ButtonType.NEGATIVE -> data.buttonNegative ButtonType.NEGATIVE -> data.buttonNegative
ButtonType.IDGAF -> data.buttonIDGAF
} }
ButtonBuilder(button).apply(builder) button.apply(builder)
} }
class DialogItem( class DialogItem(
@ -190,43 +168,32 @@ class MagiskDialog(
override val layoutRes = R.layout.item_list_single_line override val layoutRes = R.layout.item_list_single_line
} }
interface ActualOnDialogClickListener { fun interface DialogClickListener {
fun onClick(position: Int) fun onClick(position: Int)
} }
fun applyAdapter( fun setListItems(
list: Array<out CharSequence>, list: Array<out CharSequence>,
listener: OnDialogClickListener listener: DialogClickListener
) = applyView( ) = setView(
RecyclerView(context).also { RecyclerView(context).also {
it.isNestedScrollingEnabled = false it.isNestedScrollingEnabled = false
it.layoutManager = LinearLayoutManager(context) it.layoutManager = LinearLayoutManager(context)
val actualListener = object : ActualOnDialogClickListener {
override fun onClick(position: Int) {
listener(position)
dismiss()
}
}
val items = list.mapIndexed { i, cs -> DialogItem(cs, i) } val items = list.mapIndexed { i, cs -> DialogItem(cs, i) }
val binding = itemBindingOf<DialogItem> { it.bindExtra(BR.listener, actualListener) } val binding = itemBindingOf<DialogItem> { item ->
.let { ItemBinding.of(it) } item.bindExtra(BR.listener, DialogClickListener { pos ->
listener.onClick(pos)
dismiss()
})
}.let { b -> ItemBinding.of(b) }
BindingRecyclerViewAdapters.setAdapter(it, binding, items, null, null, null, null) BindingRecyclerViewAdapters.setAdapter(it, binding, items, null, null, null, null)
} }
) )
fun cancellable(isCancellable: Boolean) = apply { fun setView(view: View) {
setCancelable(isCancellable) binding.dialogBaseContainer.removeAllViews()
}
fun <Binding : ViewDataBinding> applyView(
binding: Binding,
body: Binding.() -> Unit = {}
) = applyView(binding.root).also { binding.apply(body) }
fun applyView(view: View) = apply {
resetView()
binding.dialogBaseContainer.addView( binding.dialogBaseContainer.addView(
view, view,
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@ -234,57 +201,24 @@ class MagiskDialog(
) )
} }
fun onCancel(callback: OnDialogButtonClickListener) = fun resetButtons() {
apply { setOnCancelListener(callback) }
fun onDismiss(callback: OnDialogButtonClickListener) =
apply { setOnDismissListener(callback) }
fun onShow(callback: OnDialogButtonClickListener) =
apply { setOnShowListener(callback) }
fun reveal() = apply { super.show() }
// ---
fun resetView() = apply {
binding.dialogBaseContainer.removeAllViews()
}
fun resetTitle() = applyTitle("")
fun resetMessage() = applyMessage("")
fun resetIcon() = apply { data.icon = null }
fun resetButtons() = apply {
ButtonType.values().forEach { ButtonType.values().forEach {
applyButton(it) { setButton(it) {
title = "" text = ""
icon = 0 icon = 0
isEnabled = true isEnabled = true
preventDismiss = false doNotDismiss = false
onClick {} onClick {}
} }
} }
} }
fun reset() = resetTitle() // Prevent calling setContentView
.resetMessage()
.resetView()
.resetIcon()
.resetButtons()
//region Deprecated Members @Deprecated("Please use setView(view)", level = DeprecationLevel.ERROR)
@Deprecated("Use applyTitle instead", ReplaceWith("applyTitle")) override fun setContentView(layoutResID: Int) {}
override fun setTitle(title: CharSequence?) = Unit @Deprecated("Please use setView(view)", level = DeprecationLevel.ERROR)
override fun setContentView(view: View) {}
@Deprecated("Use applyTitle instead", ReplaceWith("applyTitle")) @Deprecated("Please use setView(view)", level = DeprecationLevel.ERROR)
override fun setTitle(titleId: Int) = Unit override fun setContentView(view: View, params: ViewGroup.LayoutParams?) {}
@Deprecated("Use reveal()", ReplaceWith("reveal()"))
override fun show() {
}
//endregion
} }
typealias OnDialogButtonClickListener = (DialogInterface) -> Unit
typealias OnDialogClickListener = (position: Int) -> Unit

View File

@ -116,34 +116,18 @@
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"> app:layout_constraintStart_toStartOf="parent">
<Button
android:id="@+id/dialog_base_button_4"
style="@style/WidgetFoundation.Button.Text"
gone="@{data.buttonIDGAF.icon == 0 &amp;&amp; data.buttonIDGAF.title.length == 0}"
isEnabled="@{data.buttonIDGAF.isEnabled()}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:clickable="@{data.buttonIDGAF.isEnabled()}"
android:focusable="@{data.buttonIDGAF.isEnabled()}"
android:onClick="@{() -> data.buttonIDGAF.clicked()}"
android:text="@{data.buttonIDGAF.title}"
app:icon="@{data.buttonIDGAF.icon}"
tools:icon="@drawable/ic_bug_md2"
tools:text="Button 1" />
<Button <Button
android:id="@+id/dialog_base_button_2" android:id="@+id/dialog_base_button_2"
style="@style/WidgetFoundation.Button.Text" style="@style/WidgetFoundation.Button.Text"
gone="@{data.buttonNeutral.icon == 0 &amp;&amp; data.buttonNeutral.title.length == 0}" gone="@{data.buttonNeutral.gone}"
isEnabled="@{data.buttonNeutral.isEnabled()}" isEnabled="@{data.buttonNeutral.isEnabled}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:clickable="@{data.buttonNeutral.isEnabled()}" android:clickable="@{data.buttonNeutral.isEnabled}"
android:focusable="@{data.buttonNeutral.isEnabled()}" android:focusable="@{data.buttonNeutral.isEnabled}"
android:onClick="@{() -> data.buttonNeutral.clicked()}" android:onClick="@{() -> data.buttonNeutral.clicked()}"
android:text="@{data.buttonNeutral.title}" android:text="@{data.buttonNeutral.message}"
app:icon="@{data.buttonNeutral.icon}" app:icon="@{data.buttonNeutral.icon}"
app:iconGravity="textStart" app:iconGravity="textStart"
tools:icon="@drawable/ic_bug_md2" tools:icon="@drawable/ic_bug_md2"
@ -159,15 +143,15 @@
<Button <Button
android:id="@+id/dialog_base_button_3" android:id="@+id/dialog_base_button_3"
style="@style/WidgetFoundation.Button.Text" style="@style/WidgetFoundation.Button.Text"
gone="@{data.buttonNegative.icon == 0 &amp;&amp; data.buttonNegative.title.length == 0}" gone="@{data.buttonNegative.gone}"
isEnabled="@{data.buttonNegative.isEnabled()}" isEnabled="@{data.buttonNegative.isEnabled}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:clickable="@{data.buttonNegative.isEnabled()}" android:clickable="@{data.buttonNegative.isEnabled}"
android:focusable="@{data.buttonNegative.isEnabled()}" android:focusable="@{data.buttonNegative.isEnabled}"
android:onClick="@{() -> data.buttonNegative.clicked()}" android:onClick="@{() -> data.buttonNegative.clicked()}"
android:text="@{data.buttonNegative.title}" android:text="@{data.buttonNegative.message}"
app:icon="@{data.buttonNegative.icon}" app:icon="@{data.buttonNegative.icon}"
tools:icon="@drawable/ic_bug_md2" tools:icon="@drawable/ic_bug_md2"
tools:text="Button 1" /> tools:text="Button 1" />
@ -175,15 +159,15 @@
<Button <Button
android:id="@+id/dialog_base_button_1" android:id="@+id/dialog_base_button_1"
style="@style/WidgetFoundation.Button.Text" style="@style/WidgetFoundation.Button.Text"
gone="@{data.buttonPositive.icon == 0 &amp;&amp; data.buttonPositive.title.length == 0}" gone="@{data.buttonPositive.gone}"
isEnabled="@{data.buttonPositive.isEnabled()}" isEnabled="@{data.buttonPositive.isEnabled}"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:clickable="@{data.buttonPositive.isEnabled()}" android:clickable="@{data.buttonPositive.isEnabled}"
android:focusable="@{data.buttonPositive.isEnabled()}" android:focusable="@{data.buttonPositive.isEnabled}"
android:onClick="@{() -> data.buttonPositive.clicked()}" android:onClick="@{() -> data.buttonPositive.clicked()}"
android:text="@{data.buttonPositive.title}" android:text="@{data.buttonPositive.message}"
app:icon="@{data.buttonPositive.icon}" app:icon="@{data.buttonPositive.icon}"
app:iconGravity="textStart" app:iconGravity="textStart"
tools:icon="@drawable/ic_bug_md2" tools:icon="@drawable/ic_bug_md2"

View File

@ -10,7 +10,7 @@
<variable <variable
name="listener" name="listener"
type="com.topjohnwu.magisk.view.MagiskDialog.ActualOnDialogClickListener" /> type="com.topjohnwu.magisk.view.MagiskDialog.DialogClickListener" />
</data> </data>