Reduce usage of ObservableField

This commit is contained in:
topjohnwu 2020-07-15 02:52:15 -07:00
parent 6c6368fd81
commit ec2d7d77eb
11 changed files with 130 additions and 148 deletions

View File

@ -1,11 +1,10 @@
package com.topjohnwu.magisk.core package com.topjohnwu.magisk.core
import androidx.databinding.ObservableField import androidx.databinding.ObservableBoolean
import com.topjohnwu.magisk.DynAPK import com.topjohnwu.magisk.DynAPK
import com.topjohnwu.magisk.core.model.UpdateInfo import com.topjohnwu.magisk.core.model.UpdateInfo
import com.topjohnwu.magisk.core.net.NetworkObserver import com.topjohnwu.magisk.core.net.NetworkObserver
import com.topjohnwu.magisk.ktx.get import com.topjohnwu.magisk.ktx.get
import com.topjohnwu.magisk.ktx.value
import com.topjohnwu.magisk.utils.CachedValue import com.topjohnwu.magisk.utils.CachedValue
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import com.topjohnwu.superuser.ShellUtils.fastCmd import com.topjohnwu.superuser.ShellUtils.fastCmd
@ -36,9 +35,9 @@ object Info {
@JvmStatic var ramdisk = false @JvmStatic var ramdisk = false
val isConnected by lazy { val isConnected by lazy {
ObservableField(false).also { field -> ObservableBoolean(false).also { field ->
NetworkObserver.observe(get()) { NetworkObserver.observe(get()) {
UiThreadHandler.run { field.value = it.isAvailable } UiThreadHandler.run { field.set(it.isAvailable) }
} }
} }
} }

View File

@ -1,65 +0,0 @@
package com.topjohnwu.magisk.ktx
import androidx.databinding.Observable
import androidx.databinding.ObservableBoolean
import androidx.databinding.ObservableField
import androidx.databinding.ObservableInt
fun <T> ObservableField<T>.addOnPropertyChangedCallback(
removeAfterChanged: Boolean = false,
callback: (T?) -> Unit
) {
addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
callback(get())
if (removeAfterChanged) removeOnPropertyChangedCallback(this)
}
})
}
fun ObservableInt.addOnPropertyChangedCallback(
removeAfterChanged: Boolean = false,
callback: (Int) -> Unit
) {
addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
callback(get())
if (removeAfterChanged) removeOnPropertyChangedCallback(this)
}
})
}
fun ObservableBoolean.addOnPropertyChangedCallback(
removeAfterChanged: Boolean = false,
callback: (Boolean) -> Unit
) {
addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
callback(get())
if (removeAfterChanged) removeOnPropertyChangedCallback(this)
}
})
}
inline fun <T> ObservableField<T>.update(block: (T?) -> Unit) {
set(get().apply(block))
}
inline fun <T> ObservableField<T>.updateNonNull(block: (T) -> Unit) {
update {
it ?: return@update
block(it)
}
}
inline fun ObservableInt.update(block: (Int) -> Unit) {
set(get().apply(block))
}
inline var <T> ObservableField<T>.value
get() = get() as T
set(value) {
// Use Kotlin comparision (Any.equals)
if (value != get())
set(value)
}

View File

@ -1,13 +1,7 @@
package com.topjohnwu.magisk.ktx package com.topjohnwu.magisk.ktx
import androidx.databinding.ObservableField
import androidx.databinding.ObservableList import androidx.databinding.ObservableList
fun ObservableField<Boolean>.toggle() {
value = !value
}
fun <T> ObservableList<T>.addOnListChangedCallback( fun <T> ObservableList<T>.addOnListChangedCallback(
onChanged: ((sender: ObservableList<T>) -> Unit)? = null, onChanged: ((sender: ObservableList<T>) -> Unit)? = null,
onItemRangeRemoved: ((sender: ObservableList<T>, positionStart: Int, itemCount: Int) -> Unit)? = null, onItemRangeRemoved: ((sender: ObservableList<T>, positionStart: Int, itemCount: Int) -> Unit)? = null,

View File

@ -2,54 +2,57 @@ package com.topjohnwu.magisk.model.entity.recycler
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.databinding.ObservableField import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.databinding.ObservableItem
import com.topjohnwu.magisk.ktx.addOnPropertyChangedCallback
import com.topjohnwu.magisk.ktx.startAnimations import com.topjohnwu.magisk.ktx.startAnimations
import com.topjohnwu.magisk.ktx.toggle
import com.topjohnwu.magisk.ktx.value
import com.topjohnwu.magisk.model.entity.ProcessHideApp import com.topjohnwu.magisk.model.entity.ProcessHideApp
import com.topjohnwu.magisk.model.entity.StatefulProcess import com.topjohnwu.magisk.model.entity.StatefulProcess
import com.topjohnwu.magisk.model.observer.Observer
import com.topjohnwu.magisk.ui.hide.HideViewModel import com.topjohnwu.magisk.ui.hide.HideViewModel
import com.topjohnwu.magisk.utils.addOnPropertyChangedCallback
import com.topjohnwu.magisk.utils.set
import kotlin.math.roundToInt import kotlin.math.roundToInt
class HideItem(val item: ProcessHideApp) : ComparableRvItem<HideItem>() { class HideItem(val item: ProcessHideApp) : ObservableItem<HideItem>() {
override val layoutRes = R.layout.item_hide_md2 override val layoutRes = R.layout.item_hide_md2
val packageName = item.info.info.packageName.orEmpty() val packageName = item.info.info.packageName.orEmpty()
val items = item.processes.map { HideProcessItem(it) } val items = item.processes.map { HideProcessItem(it) }
val isExpanded = ObservableField(false) @get:Bindable
val itemsChecked = ObservableField(0) var isExpanded = false
val itemsCheckedPercent = Observer(itemsChecked) { set(value) = set(value, field, { field = it }, BR.expanded)
(itemsChecked.value.toFloat() / items.size * 100).roundToInt()
}
/** [toggle] depends on this functionality */ @get:Bindable
private val isHidden get() = itemsChecked.value == items.size var itemsChecked = 0
set(value) = set(value, field, { field = it }, BR.itemsChecked, BR.itemsCheckedPercent)
@get:Bindable
val itemsCheckedPercent get() = (itemsChecked.toFloat() / items.size * 100).roundToInt()
private val isHidden get() = itemsChecked == items.size
init { init {
items.forEach { it.isHidden.addOnPropertyChangedCallback { recalculateChecked() } } items.forEach { it.addOnPropertyChangedCallback(BR.hidden) { recalculateChecked() } }
recalculateChecked() recalculateChecked()
} }
fun collapse(v: View) { fun collapse(v: View) {
(v.parent.parent as? ViewGroup)?.startAnimations() (v.parent.parent as? ViewGroup)?.startAnimations()
isExpanded.value = false isExpanded = false
} }
fun toggle(v: View) { fun toggle(v: View) {
(v.parent as? ViewGroup)?.startAnimations() (v.parent as? ViewGroup)?.startAnimations()
isExpanded.toggle() isExpanded = !isExpanded
} }
fun toggle(viewModel: HideViewModel): Boolean { fun toggle(viewModel: HideViewModel): Boolean {
// contract implies that isHidden == all checked // contract implies that isHidden == all checked
if (!isHidden) { if (!isHidden) {
items.filterNot { it.isHidden.value } items.filterNot { it.isHidden }
} else { } else {
items items
}.forEach { it.toggle(viewModel) } }.forEach { it.toggle(viewModel) }
@ -57,7 +60,7 @@ class HideItem(val item: ProcessHideApp) : ComparableRvItem<HideItem>() {
} }
private fun recalculateChecked() { private fun recalculateChecked() {
itemsChecked.value = items.count { it.isHidden.value } itemsChecked = items.count { it.isHidden }
} }
override fun contentSameAs(other: HideItem): Boolean = item == other.item override fun contentSameAs(other: HideItem): Boolean = item == other.item
@ -65,14 +68,17 @@ class HideItem(val item: ProcessHideApp) : ComparableRvItem<HideItem>() {
} }
class HideProcessItem(val item: StatefulProcess) : ComparableRvItem<HideProcessItem>() { class HideProcessItem(val item: StatefulProcess) : ObservableItem<HideProcessItem>() {
override val layoutRes = R.layout.item_hide_process_md2 override val layoutRes = R.layout.item_hide_process_md2
val isHidden = ObservableField(item.isHidden) @get:Bindable
var isHidden = item.isHidden
set(value) = set(value, field, { field = it }, BR.hidden)
fun toggle(viewModel: HideViewModel) { fun toggle(viewModel: HideViewModel) {
isHidden.toggle() isHidden = !isHidden
viewModel.toggleItem(this) viewModel.toggleItem(this)
} }

View File

@ -1,51 +1,62 @@
package com.topjohnwu.magisk.model.entity.recycler package com.topjohnwu.magisk.model.entity.recycler
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import androidx.databinding.ObservableField import androidx.databinding.Bindable
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.StaggeredGridLayoutManager import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.core.model.MagiskPolicy import com.topjohnwu.magisk.core.model.MagiskPolicy
import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.databinding.ObservableItem
import com.topjohnwu.magisk.ktx.toggle
import com.topjohnwu.magisk.ktx.value
import com.topjohnwu.magisk.ui.superuser.SuperuserViewModel import com.topjohnwu.magisk.ui.superuser.SuperuserViewModel
import com.topjohnwu.magisk.utils.set
class PolicyItem(val item: MagiskPolicy, val icon: Drawable) : ComparableRvItem<PolicyItem>() { class PolicyItem(val item: MagiskPolicy, val icon: Drawable) : ObservableItem<PolicyItem>() {
override val layoutRes = R.layout.item_policy_md2 override val layoutRes = R.layout.item_policy_md2
val isExpanded = ObservableField(false) @get:Bindable
val isEnabled = ObservableField(item.policy == MagiskPolicy.ALLOW) var isExpanded = false
val shouldNotify = ObservableField(item.notification) set(value) = set(value, field, { field = it }, BR.expanded)
val shouldLog = ObservableField(item.logging)
@get:Bindable
var isEnabled = item.policy == MagiskPolicy.ALLOW
set(value) = set(value, field, { field = it }, BR.enabled)
@get:Bindable
var shouldNotify = item.notification
set(value) = set(value, field, { field = it }, BR.shouldNotify)
@get:Bindable
var shouldLog = item.logging
set(value) = set(value, field, { field = it }, BR.shouldLog)
private val updatedPolicy private val updatedPolicy
get() = item.copy( get() = item.copy(
policy = if (isEnabled.value) MagiskPolicy.ALLOW else MagiskPolicy.DENY, policy = if (isEnabled) MagiskPolicy.ALLOW else MagiskPolicy.DENY,
notification = shouldNotify.value, notification = shouldNotify,
logging = shouldLog.value logging = shouldLog
) )
fun toggle(viewModel: SuperuserViewModel) { fun toggle(viewModel: SuperuserViewModel) {
if (isExpanded.value) { if (isExpanded) {
toggle() toggle()
return return
} }
isEnabled.toggle() isEnabled = !isEnabled
viewModel.togglePolicy(this, isEnabled.value) viewModel.togglePolicy(this, isEnabled)
} }
fun toggle() { fun toggle() {
isExpanded.toggle() isExpanded = !isExpanded
} }
fun toggleNotify(viewModel: SuperuserViewModel) { fun toggleNotify(viewModel: SuperuserViewModel) {
shouldNotify.toggle() shouldNotify = !shouldNotify
viewModel.updatePolicy(updatedPolicy, isLogging = false) viewModel.updatePolicy(updatedPolicy, isLogging = false)
} }
fun toggleLog(viewModel: SuperuserViewModel) { fun toggleLog(viewModel: SuperuserViewModel) {
shouldLog.toggle() shouldLog = !shouldLog
viewModel.updatePolicy(updatedPolicy, isLogging = true) viewModel.updatePolicy(updatedPolicy, isLogging = true)
} }

View File

@ -6,7 +6,6 @@ import androidx.lifecycle.viewModelScope
import com.topjohnwu.magisk.BR import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.core.utils.currentLocale import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.data.repository.MagiskRepository import com.topjohnwu.magisk.data.repository.MagiskRepository
import com.topjohnwu.magisk.ktx.value
import com.topjohnwu.magisk.model.entity.HideAppInfo import com.topjohnwu.magisk.model.entity.HideAppInfo
import com.topjohnwu.magisk.model.entity.HideTarget import com.topjohnwu.magisk.model.entity.HideTarget
import com.topjohnwu.magisk.model.entity.ProcessHideApp import com.topjohnwu.magisk.model.entity.ProcessHideApp
@ -71,7 +70,7 @@ class HideViewModel(
return ProcessHideApp(a, processes) return ProcessHideApp(a, processes)
} }
private fun List<HideItem>.sort() = compareByDescending<HideItem> { it.itemsChecked.value } private fun List<HideItem>.sort() = compareByDescending<HideItem> { it.itemsChecked }
.thenBy { it.item.info.name.toLowerCase(currentLocale) } .thenBy { it.item.info.name.toLowerCase(currentLocale) }
.thenBy { it.item.info.info.packageName } .thenBy { it.item.info.info.packageName }
.let { sortedWith(it) } .let { sortedWith(it) }
@ -96,7 +95,7 @@ class HideViewModel(
// --- // ---
fun toggleItem(item: HideProcessItem) = magiskRepo fun toggleItem(item: HideProcessItem) = magiskRepo
.toggleHide(item.isHidden.value, item.item.packageName, item.item.name) .toggleHide(item.isHidden, item.item.packageName, item.item.name)
fun resetQuery() { fun resetQuery() {
query = "" query = ""

View File

@ -15,7 +15,6 @@ import com.topjohnwu.magisk.data.repository.MagiskRepository
import com.topjohnwu.magisk.ktx.await import com.topjohnwu.magisk.ktx.await
import com.topjohnwu.magisk.ktx.packageName import com.topjohnwu.magisk.ktx.packageName
import com.topjohnwu.magisk.ktx.res import com.topjohnwu.magisk.ktx.res
import com.topjohnwu.magisk.ktx.value
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Manager import com.topjohnwu.magisk.model.entity.internal.DownloadSubject.Manager
import com.topjohnwu.magisk.model.entity.recycler.DeveloperItem import com.topjohnwu.magisk.model.entity.recycler.DeveloperItem
import com.topjohnwu.magisk.model.entity.recycler.HomeItem import com.topjohnwu.magisk.model.entity.recycler.HomeItem
@ -101,7 +100,7 @@ class HomeViewModel(
} }
stateManager = when { stateManager = when {
!app.isUpdateChannelCorrect && isConnected.value -> MagiskState.NOT_INSTALLED !app.isUpdateChannelCorrect && isConnected.get() -> MagiskState.NOT_INSTALLED
app.isObsolete -> MagiskState.OBSOLETE app.isObsolete -> MagiskState.OBSOLETE
else -> MagiskState.UP_TO_DATE else -> MagiskState.UP_TO_DATE
} }

View File

@ -12,7 +12,6 @@ import com.topjohnwu.magisk.core.download.DownloadService
import com.topjohnwu.magisk.core.utils.PatchAPK import com.topjohnwu.magisk.core.utils.PatchAPK
import com.topjohnwu.magisk.core.utils.Utils import com.topjohnwu.magisk.core.utils.Utils
import com.topjohnwu.magisk.data.database.RepoDao import com.topjohnwu.magisk.data.database.RepoDao
import com.topjohnwu.magisk.ktx.value
import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.Configuration
import com.topjohnwu.magisk.model.entity.internal.DownloadSubject import com.topjohnwu.magisk.model.entity.internal.DownloadSubject
import com.topjohnwu.magisk.model.entity.recycler.SettingsItem import com.topjohnwu.magisk.model.entity.recycler.SettingsItem
@ -56,7 +55,7 @@ class SettingsViewModel(
)) ))
if (Info.env.isActive) { if (Info.env.isActive) {
list.add(ClearRepoCache) list.add(ClearRepoCache)
if (Const.USER_ID == 0 && Info.isConnected.value) if (Const.USER_ID == 0 && Info.isConnected.get())
list.add(HideOrRestore()) list.add(HideOrRestore())
} }

View File

@ -11,7 +11,6 @@ import com.topjohnwu.magisk.core.model.MagiskPolicy
import com.topjohnwu.magisk.core.utils.BiometricHelper import com.topjohnwu.magisk.core.utils.BiometricHelper
import com.topjohnwu.magisk.core.utils.currentLocale import com.topjohnwu.magisk.core.utils.currentLocale
import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.ktx.toggle
import com.topjohnwu.magisk.model.entity.recycler.PolicyItem import com.topjohnwu.magisk.model.entity.recycler.PolicyItem
import com.topjohnwu.magisk.model.entity.recycler.TappableHeadlineItem import com.topjohnwu.magisk.model.entity.recycler.TappableHeadlineItem
import com.topjohnwu.magisk.model.entity.recycler.TextItem import com.topjohnwu.magisk.model.entity.recycler.TextItem
@ -140,7 +139,7 @@ class SuperuserViewModel(
if (BiometricHelper.isEnabled) { if (BiometricHelper.isEnabled) {
BiometricDialog { BiometricDialog {
onSuccess { updateState() } onSuccess { updateState() }
onFailure { item.isEnabled.toggle() } onFailure { item.isEnabled = !item.isEnabled }
}.publish() }.publish()
} else { } else {
updateState() updateState()

View File

@ -49,6 +49,22 @@ interface ObservableHost : Observable {
} }
} }
fun ObservableHost.addOnPropertyChangedCallback(
fieldId: Int,
removeAfterChanged: Boolean = false,
callback: () -> Unit
) {
addOnPropertyChangedCallback(object : Observable.OnPropertyChangedCallback() {
override fun onPropertyChanged(sender: Observable?, propertyId: Int) {
if (fieldId == propertyId) {
callback()
if (removeAfterChanged)
removeOnPropertyChangedCallback(this)
}
}
})
}
/** /**
* Injects boilerplate implementation for {@literal @}[androidx.databinding.Bindable] field setters. * Injects boilerplate implementation for {@literal @}[androidx.databinding.Bindable] field setters.
* *

View File

@ -15,7 +15,8 @@ import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatDialog import androidx.appcompat.app.AppCompatDialog
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.databinding.ObservableField import androidx.databinding.Bindable
import androidx.databinding.PropertyChangeRegistry
import androidx.databinding.ViewDataBinding import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -23,8 +24,9 @@ import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.databinding.ComparableRvItem
import com.topjohnwu.magisk.databinding.DialogMagiskBaseBinding import com.topjohnwu.magisk.databinding.DialogMagiskBaseBinding
import com.topjohnwu.magisk.ktx.value
import com.topjohnwu.magisk.ui.base.itemBindingOf import com.topjohnwu.magisk.ui.base.itemBindingOf
import com.topjohnwu.magisk.utils.ObservableHost
import com.topjohnwu.magisk.utils.set
import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapters import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapters
import me.tatarka.bindingcollectionadapter2.ItemBinding import me.tatarka.bindingcollectionadapter2.ItemBinding
@ -71,11 +73,24 @@ class MagiskDialog(
binding.dialogBaseOutsideContainer.setOnClickListener(listener) binding.dialogBaseOutsideContainer.setOnClickListener(listener)
} }
inner class Data { inner class Data: ObservableHost {
val icon = ObservableField(0) override var callbacks: PropertyChangeRegistry? = null
val iconRaw = ObservableField(null as Drawable?)
val title = ObservableField<CharSequence>("") @get:Bindable
val message = ObservableField<CharSequence>("") var icon = 0
set(value) = set(value, field, { field = it }, BR.icon)
@get:Bindable
var iconRaw: Drawable? = null
set(value) = set(value, field, { field = it }, BR.iconRaw)
@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 = Button() val buttonPositive = Button()
val buttonNeutral = Button() val buttonNeutral = Button()
@ -87,10 +102,20 @@ class MagiskDialog(
POSITIVE, NEUTRAL, NEGATIVE, IDGAF POSITIVE, NEUTRAL, NEGATIVE, IDGAF
} }
inner class Button { inner class Button: ObservableHost {
val icon = ObservableField(0) override var callbacks: PropertyChangeRegistry? = null
val title = ObservableField<CharSequence>("")
val isEnabled = ObservableField(true) @get:Bindable
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 onClickAction: OnDialogButtonClickListener = {}
var preventDismiss = false var preventDismiss = false
@ -117,24 +142,24 @@ class MagiskDialog(
inner class ButtonBuilder(private val button: Button) { inner class ButtonBuilder(private val button: Button) {
var icon: Int var icon: Int
get() = button.icon.value get() = button.icon
set(value) { set(value) {
button.icon.value = value button.icon = value
} }
var title: CharSequence var title: CharSequence
get() = button.title.value get() = button.title
set(value) { set(value) {
button.title.value = value button.title = value
} }
var titleRes: Int var titleRes: Int
get() = 0 get() = 0
set(value) { set(value) {
button.title.value = context.getString(value) button.title = context.getString(value)
} }
var isEnabled: Boolean var isEnabled: Boolean
get() = button.isEnabled.value get() = button.isEnabled
set(value) { set(value) {
button.isEnabled.value = value button.isEnabled = value
} }
var preventDismiss: Boolean var preventDismiss: Boolean
get() = button.preventDismiss get() = button.preventDismiss
@ -148,22 +173,22 @@ class MagiskDialog(
} }
fun applyTitle(@StringRes stringRes: Int) = fun applyTitle(@StringRes stringRes: Int) =
apply { data.title.value = context.getString(stringRes) } apply { data.title = context.getString(stringRes) }
fun applyTitle(title: CharSequence) = fun applyTitle(title: CharSequence) =
apply { data.title.value = title } apply { data.title = title }
fun applyMessage(@StringRes stringRes: Int, vararg args: Any) = fun applyMessage(@StringRes stringRes: Int, vararg args: Any) =
apply { data.message.value = context.getString(stringRes, *args) } apply { data.message = context.getString(stringRes, *args) }
fun applyMessage(message: CharSequence) = fun applyMessage(message: CharSequence) =
apply { data.message.value = message } apply { data.message = message }
fun applyIcon(@DrawableRes drawableRes: Int) = fun applyIcon(@DrawableRes drawableRes: Int) =
apply { data.icon.value = drawableRes } apply { data.icon = drawableRes }
fun applyIcon(drawable: Drawable) = fun applyIcon(drawable: Drawable) =
apply { data.iconRaw.value = drawable } apply { data.iconRaw = drawable }
fun applyButton(buttonType: ButtonType, builder: ButtonBuilder.() -> Unit) = apply { fun applyButton(buttonType: ButtonType, builder: ButtonBuilder.() -> Unit) = apply {
val button = when (buttonType) { val button = when (buttonType) {