Magisk/app/src/main/java/com/topjohnwu/magisk/ui/settings/BaseSettingsItem.kt

177 lines
5.6 KiB
Kotlin
Raw Normal View History

package com.topjohnwu.magisk.ui.settings
import android.content.Context
import android.content.res.Resources
import android.view.View
import androidx.annotation.CallSuper
import androidx.databinding.Bindable
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
2020-07-12 12:17:50 +02:00
import com.topjohnwu.magisk.databinding.ObservableItem
2021-04-09 10:32:37 +02:00
import com.topjohnwu.magisk.utils.TextHolder
2020-07-15 10:21:57 +02:00
import com.topjohnwu.magisk.utils.set
import com.topjohnwu.magisk.view.MagiskDialog
sealed class BaseSettingsItem : ObservableItem<BaseSettingsItem>() {
2020-08-11 12:30:00 +02:00
override val layoutRes get() = R.layout.item_settings
2020-01-17 10:02:40 +01:00
open val icon: Int get() = 0
2021-04-09 10:32:37 +02:00
open val title: TextHolder get() = TextHolder.EMPTY
2020-01-17 10:02:40 +01:00
@get:Bindable
2021-04-09 10:32:37 +02:00
open val description: TextHolder get() = TextHolder.EMPTY
2020-01-17 10:02:40 +01:00
2020-08-11 12:30:00 +02:00
// ---
open val showSwitch get() = false
@get:Bindable
open val isChecked get() = false
open fun onToggle(view: View, callback: Callback, checked: Boolean) {}
// ---
2020-01-17 10:02:40 +01:00
@get:Bindable
2020-07-15 10:21:57 +02:00
var isEnabled = true
set(value) = set(value, field, { field = it }, BR.enabled)
open fun onPressed(view: View, callback: Callback) {
callback.onItemPressed(view, this)
}
open fun refresh() {}
override fun itemSameAs(other: BaseSettingsItem) = this === other
override fun contentSameAs(other: BaseSettingsItem) = itemSameAs(other)
// ---
interface Callback {
fun onItemPressed(view: View, item: BaseSettingsItem, callback: () -> Unit = {})
fun onItemChanged(view: View, item: BaseSettingsItem)
}
// ---
abstract class Value<T> : BaseSettingsItem() {
/**
* Represents last agreed-upon value by the validation process and the user for current
* child. Be very aware that this shouldn't be **set** unless both sides agreed that _that_
* is the new value.
*
* Annotating [value] as [Bindable] property should raise red flags immediately. If you
* need a [Bindable] property create another one. Seriously.
* */
abstract var value: T
/**
* We don't want this to be accessible to be set from outside the instances. It will
* introduce unwanted bugs!
* */
protected set
protected var callbackVars: Pair<View, Callback>? = null
@CallSuper
override fun onPressed(view: View, callback: Callback) {
callbackVars = view to callback
callback.onItemPressed(view, this) {
onPressed(view)
}
}
abstract fun onPressed(view: View)
2020-07-15 10:21:57 +02:00
protected inline fun <reified T> setV(
new: T, old: T, setter: (T) -> Unit, afterChanged: (T) -> Unit = {}) {
set(new, old, setter, BR.description, BR.checked) {
afterChanged(it)
2020-08-11 12:30:00 +02:00
callbackVars?.let { (view, callback) ->
callbackVars = null
2020-08-11 12:30:00 +02:00
callback.onItemChanged(view, this)
}
}
2020-07-15 10:21:57 +02:00
}
}
abstract class Toggle : Value<Boolean>() {
2020-08-11 12:30:00 +02:00
override val showSwitch get() = true
override val isChecked get() = value
override fun onToggle(view: View, callback: Callback, checked: Boolean) =
set(checked, value, { onPressed(view, callback) }, BR.checked)
override fun onPressed(view: View) {
value = !value
}
}
2020-08-11 09:54:19 +02:00
abstract class Input : Value<String>() {
2020-08-11 09:54:19 +02:00
protected abstract val inputResult: String?
override fun onPressed(view: View) {
MagiskDialog(view.context)
2020-08-11 09:54:19 +02:00
.applyTitle(title.getText(view.resources))
.applyView(getView(view.context))
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = android.R.string.ok
onClick {
2020-08-11 09:54:19 +02:00
inputResult?.let { result ->
preventDismiss = false
value = result
it.dismiss()
return@onClick
}
preventDismiss = true
}
}
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel
}
.reveal()
}
abstract fun getView(context: Context): View
}
abstract class Selector : Value<Int>() {
open val entryRes get() = -1
open val descriptionRes get() = entryRes
open fun entries(res: Resources) = res.getArrayOrEmpty(entryRes)
open fun descriptions(res: Resources) = res.getArrayOrEmpty(descriptionRes)
override val description = object : TextHolder() {
override fun getText(resources: Resources): CharSequence {
return descriptions(resources).getOrElse(value) { "" }
}
}
2020-02-18 23:02:08 +01:00
private fun Resources.getArrayOrEmpty(id: Int): Array<String> =
runCatching { getStringArray(id) }.getOrDefault(emptyArray())
override fun onPressed(view: View) {
MagiskDialog(view.context)
.applyTitle(title.getText(view.resources))
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel
}
.applyAdapter(entries(view.resources)) {
value = it
}
.reveal()
}
}
abstract class Blank : BaseSettingsItem()
abstract class Section : BaseSettingsItem() {
override val layoutRes = R.layout.item_settings_section
}
2020-01-17 10:02:40 +01:00
}