Cleanup setting items

This commit is contained in:
topjohnwu 2020-08-11 03:30:00 -07:00
parent 87b6bf2c26
commit 537ae1a315
7 changed files with 54 additions and 358 deletions

View File

@ -18,12 +18,24 @@ import org.koin.core.get
sealed class SettingsItem : ObservableItem<SettingsItem>() { sealed class SettingsItem : ObservableItem<SettingsItem>() {
override val layoutRes get() = R.layout.item_settings
open val icon: Int get() = 0 open val icon: Int get() = 0
open val title: TransitiveText get() = TransitiveText.EMPTY open val title: TransitiveText get() = TransitiveText.EMPTY
@get:Bindable @get:Bindable
open val description: TransitiveText get() = TransitiveText.EMPTY open val description: TransitiveText get() = TransitiveText.EMPTY
// ---
open val showSwitch get() = false
@get:Bindable
open val isChecked get() = false
open fun onToggle(view: View, callback: Callback, checked: Boolean) {}
// ---
@get:Bindable @get:Bindable
var isEnabled = true var isEnabled = true
set(value) = set(value, field, { field = it }, BR.enabled) set(value) = set(value, field, { field = it }, BR.enabled)
@ -48,7 +60,6 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
abstract class Value<T> : SettingsItem() { abstract class Value<T> : SettingsItem() {
@get:Bindable
abstract var value: T abstract var value: T
protected var callbackVars: Pair<View, Callback>? = null protected var callbackVars: Pair<View, Callback>? = null
@ -63,24 +74,13 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
abstract fun onPressed(view: View) abstract fun onPressed(view: View)
protected inline fun <reified T> setV(
new: T, old: T, setter: (T) -> Unit, vararg fieldIds: Int, afterChanged: (T) -> Unit = {}) {
set(new, old, setter, BR.value, *fieldIds) {
afterChanged(it)
callbackVars?.let { pair ->
callbackVars = null
pair.second.onItemChanged(pair.first, this)
}
}
}
protected inline fun <reified T> setV( protected inline fun <reified T> setV(
new: T, old: T, setter: (T) -> Unit, afterChanged: (T) -> Unit = {}) { new: T, old: T, setter: (T) -> Unit, afterChanged: (T) -> Unit = {}) {
set(new, old, setter, BR.value) { set(new, old, setter, BR.value, BR.description, BR.checked) {
afterChanged(it) afterChanged(it)
callbackVars?.let { pair -> callbackVars?.let { (view, callback) ->
callbackVars = null callbackVars = null
pair.second.onItemChanged(pair.first, this) callback.onItemChanged(view, this)
} }
} }
} }
@ -88,7 +88,11 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
abstract class Toggle : Value<Boolean>() { abstract class Toggle : Value<Boolean>() {
override val layoutRes = R.layout.item_settings_toggle 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) { override fun onPressed(view: View) {
value = !value value = !value
@ -97,8 +101,6 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
abstract class Input : Value<String>() { abstract class Input : Value<String>() {
override val layoutRes = R.layout.item_settings_input
protected abstract val inputResult: String? protected abstract val inputResult: String?
override fun onPressed(view: View) { override fun onPressed(view: View) {
@ -128,8 +130,6 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
abstract class Selector : Value<Int>(), KoinComponent { abstract class Selector : Value<Int>(), KoinComponent {
override val layoutRes = R.layout.item_settings_selector
protected val resources get() = get<Resources>() protected val resources get() = get<Resources>()
@ArrayRes open val entryRes = -1 @ArrayRes open val entryRes = -1
@ -138,15 +138,9 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
open val entries get() = resources.getArrayOrEmpty(entryRes) open val entries get() = resources.getArrayOrEmpty(entryRes)
open val entryValues get() = resources.getArrayOrEmpty(entryValRes) open val entryValues get() = resources.getArrayOrEmpty(entryValRes)
@get:Bindable
override val description: TransitiveText override val description: TransitiveText
get() = entries.getOrNull(value)?.asTransitive() ?: TransitiveText.EMPTY get() = entries.getOrNull(value)?.asTransitive() ?: TransitiveText.EMPTY
protected inline fun <reified T> setS(
new: T, old: T, setter: (T) -> Unit, afterChanged: (T) -> Unit = {}) {
setV(new, old, setter, BR.description, afterChanged = afterChanged)
}
private fun Resources.getArrayOrEmpty(id: Int): Array<String> = private fun Resources.getArrayOrEmpty(id: Int): Array<String> =
runCatching { getStringArray(id) }.getOrDefault(emptyArray()) runCatching { getStringArray(id) }.getOrDefault(emptyArray())
@ -169,9 +163,7 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
} }
abstract class Blank : SettingsItem() { abstract class Blank : SettingsItem()
override val layoutRes = R.layout.item_settings_blank
}
abstract class Section : SettingsItem() { abstract class Section : SettingsItem() {
override val layoutRes = R.layout.item_settings_section override val layoutRes = R.layout.item_settings_section

View File

@ -36,7 +36,7 @@ object Customization : SettingsItem.Section() {
object Language : SettingsItem.Selector() { object Language : SettingsItem.Selector() {
override var value = -1 override var value = -1
set(value) = setS(value, field, { field = it }) { set(value) = setV(value, field, { field = it }) {
Config.locale = entryValues[it] Config.locale = entryValues[it]
} }
@ -82,7 +82,9 @@ object Hide : SettingsItem.Input() {
@get:Bindable @get:Bindable
override var value = "Manager" override var value = "Manager"
set(value) = setV(value, field, { field = it }, BR.error) set(value) = setV(value, field, { field = it }) {
notifyPropertyChanged(BR.error)
}
@get:Bindable @get:Bindable
val isError get() = value.length > 14 || value.isBlank() val isError get() = value.length > 14 || value.isBlank()
@ -125,7 +127,7 @@ object DownloadPath : SettingsItem.Input() {
object UpdateChannel : SettingsItem.Selector() { object UpdateChannel : SettingsItem.Selector() {
override var value = Config.updateChannel override var value = Config.updateChannel
set(value) = setS(value, field, { field = it }) { Config.updateChannel = it } set(value) = setV(value, field, { field = it }) { Config.updateChannel = it }
override val title = R.string.settings_update_channel_title.asTransitive() override val title = R.string.settings_update_channel_title.asTransitive()
override val entries get() = resources.getStringArray(R.array.update_channel).let { override val entries get() = resources.getStringArray(R.array.update_channel).let {
@ -138,6 +140,7 @@ object UpdateChannelUrl : SettingsItem.Input() {
override val title = R.string.settings_update_custom.asTransitive() override val title = R.string.settings_update_custom.asTransitive()
override var value = Config.customChannelUrl override var value = Config.customChannelUrl
set(value) = setV(value, field, { field = it }) { Config.customChannelUrl = it } set(value) = setV(value, field, { field = it }) { Config.customChannelUrl = it }
override val description get() = value.asTransitive()
override val inputResult get() = result override val inputResult get() = result
@ -226,7 +229,7 @@ object AccessMode : SettingsItem.Selector() {
override val entryValRes = R.array.value_array override val entryValRes = R.array.value_array
override var value = Config.rootMode override var value = Config.rootMode
set(value) = setS(value, field, { field = it }) { set(value) = setV(value, field, { field = it }) {
Config.rootMode = entryValues[it].toInt() Config.rootMode = entryValues[it].toInt()
} }
} }
@ -237,7 +240,7 @@ object MultiuserMode : SettingsItem.Selector() {
override val entryValRes = R.array.value_array override val entryValRes = R.array.value_array
override var value = Config.suMultiuserMode override var value = Config.suMultiuserMode
set(value) = setS(value, field, { field = it }) { set(value) = setV(value, field, { field = it }) {
Config.suMultiuserMode = entryValues[it].toInt() Config.suMultiuserMode = entryValues[it].toInt()
} }
@ -255,7 +258,7 @@ object MountNamespaceMode : SettingsItem.Selector() {
override val entryValRes = R.array.value_array override val entryValRes = R.array.value_array
override var value = Config.suMntNamespaceMode override var value = Config.suMntNamespaceMode
set(value) = setS(value, field, { field = it }) { set(value) = setV(value, field, { field = it }) {
Config.suMntNamespaceMode = entryValues[it].toInt() Config.suMntNamespaceMode = entryValues[it].toInt()
} }
@ -269,7 +272,7 @@ object AutomaticResponse : SettingsItem.Selector() {
override val entryValRes = R.array.value_array override val entryValRes = R.array.value_array
override var value = Config.suAutoReponse override var value = Config.suAutoReponse
set(value) = setS(value, field, { field = it }) { set(value) = setV(value, field, { field = it }) {
Config.suAutoReponse = entryValues[it].toInt() Config.suAutoReponse = entryValues[it].toInt()
} }
} }
@ -280,7 +283,7 @@ object RequestTimeout : SettingsItem.Selector() {
override val entryValRes = R.array.request_timeout_value override val entryValRes = R.array.request_timeout_value
override var value = selected override var value = selected
set(value) = setS(value, field, { field = it }) { set(value) = setV(value, field, { field = it }) {
Config.suDefaultTimeout = entryValues[it].toInt() Config.suDefaultTimeout = entryValues[it].toInt()
} }
@ -294,7 +297,7 @@ object SUNotification : SettingsItem.Selector() {
override val entryValRes = R.array.value_array override val entryValRes = R.array.value_array
override var value = Config.suNotification override var value = Config.suNotification
set(value) = setS(value, field, { field = it }) { set(value) = setV(value, field, { field = it }) {
Config.suNotification = entryValues[it].toInt() Config.suNotification = entryValues[it].toInt()
} }
} }

View File

@ -31,7 +31,7 @@
android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l1}" android:paddingBottom="@{viewModel.insets.bottom + (int) @dimen/l1}"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:layout_marginTop="24dp" tools:layout_marginTop="24dp"
tools:listitem="@layout/item_settings_toggle" tools:listitem="@layout/item_settings"
tools:paddingTop="@dimen/l1" /> tools:paddingTop="@dimen/l1" />
</layout> </layout>

View File

@ -5,11 +5,9 @@
<data> <data>
<import type="com.topjohnwu.magisk.R" />
<variable <variable
name="item" name="item"
type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Toggle" /> type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem" />
<variable <variable
name="callback" name="callback"
@ -26,49 +24,32 @@
android:onClick="@{(view) -> item.onPressed(view, callback)}" android:onClick="@{(view) -> item.onPressed(view, callback)}"
tools:layout_gravity="center"> tools:layout_gravity="center">
<androidx.constraintlayout.widget.ConstraintLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="horizontal"
android:gravity="center_vertical"
android:paddingStart="@{item.icon == 0 ? @dimen/l1 : 0}"
android:paddingEnd="@dimen/l1">
<ImageView <ImageView
android:id="@+id/toggle_icon" android:id="@+id/icon"
style="@style/WidgetFoundation.Icon" style="@style/WidgetFoundation.Icon"
gone="@{item.icon == 0}" gone="@{item.icon == 0}"
android:background="@null" android:background="@null"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@{item.icon}" app:srcCompat="@{item.icon}"
tools:srcCompat="@drawable/ic_fingerprint" /> tools:srcCompat="@drawable/ic_fingerprint" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/toggle_indicator"
android:checked="@={item.value}"
isEnabled="@{item.enabled}"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/l_25"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<LinearLayout <LinearLayout
android:layout_weight="1"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="@{item.icon == 0 ? (int) @dimen/l1 : 0}"
android:layout_marginEnd="@dimen/l_50"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="vertical" android:orientation="vertical"
android:paddingTop="@dimen/l1" android:paddingTop="@dimen/l1"
android:paddingBottom="@dimen/l1" android:paddingBottom="@dimen/l1">
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/toggle_indicator"
app:layout_constraintStart_toEndOf="@+id/toggle_icon"
app:layout_constraintTop_toTopOf="parent">
<TextView <TextView
android:id="@+id/toggle_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="center" android:layout_gravity="center"
@ -80,7 +61,6 @@
tools:text="@tools:sample/lorem/random" /> tools:text="@tools:sample/lorem/random" />
<TextView <TextView
android:id="@+id/toggle_description"
gone="@{item.description.empty}" gone="@{item.description.empty}"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -91,7 +71,16 @@
</LinearLayout> </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> <com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/selector_indicator"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:checked="@{item.checked}"
android:onCheckedChanged="@{(v, c) -> item.onToggle(v, callback, c)}"
goneUnless="@{item.showSwitch}"
isEnabled="@{item.enabled}"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView> </com.google.android.material.card.MaterialCardView>

View File

@ -1,96 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Blank" />
<variable
name="callback"
type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Callback" />
</data>
<com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card"
isEnabled="@{item.enabled}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="@{item.enabled ? 1f : .5f}"
android:onClick="@{(view) -> item.onPressed(view, callback)}"
tools:layout_gravity="center">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/blank_icon"
style="@style/WidgetFoundation.Icon"
gone="@{item.icon == 0}"
android:background="@null"
android:rotation="180"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@{item.icon}"
tools:srcCompat="@drawable/ic_fingerprint" />
<ImageView
android:id="@+id/blank_indicator"
style="@style/WidgetFoundation.Icon"
android:background="@null"
android:rotation="180"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_back_md2" />
<LinearLayout
android:id="@+id/blank_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@{item.icon == 0 ? (int) @dimen/l1 : 0}"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingTop="@dimen/l1"
android:paddingBottom="@dimen/l1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/blank_indicator"
app:layout_constraintStart_toEndOf="@+id/blank_icon"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/blank_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="start"
android:text="@{item.title}"
android:textAppearance="@style/AppearanceFoundation.Body"
android:textStyle="bold"
tools:lines="2"
tools:text="@tools:sample/lorem/random" />
<TextView
android:id="@+id/blank_description"
gone="@{item.description.empty}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{item.description}"
android:textAppearance="@style/AppearanceFoundation.Tiny.Variant"
tools:lines="2"
tools:text="@tools:sample/lorem/random" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</layout>

View File

@ -1,96 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Input" />
<variable
name="callback"
type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Callback" />
</data>
<com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card"
isEnabled="@{item.enabled}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="@{item.enabled ? 1f : .5f}"
android:onClick="@{(view) -> item.onPressed(view, callback)}"
tools:layout_gravity="center">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/input_icon"
style="@style/WidgetFoundation.Icon"
gone="@{item.icon == 0}"
android:background="@null"
android:rotation="180"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@{item.icon}"
tools:srcCompat="@drawable/ic_fingerprint" />
<ImageView
android:id="@+id/input_indicator"
style="@style/WidgetFoundation.Icon"
android:background="@null"
android:rotation="180"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_back_md2" />
<LinearLayout
android:id="@+id/input_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@{item.icon == 0 ? (int) @dimen/l1 : 0}"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingTop="@dimen/l1"
android:paddingBottom="@dimen/l1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/input_indicator"
app:layout_constraintStart_toEndOf="@+id/input_icon"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/input_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="start"
android:text="@{item.title}"
android:textAppearance="@style/AppearanceFoundation.Body"
android:textStyle="bold"
tools:lines="1"
tools:text="@tools:sample/lorem/random" />
<TextView
android:id="@+id/input_description"
gone="@{item.description.empty}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{item.description}"
android:textAppearance="@style/AppearanceFoundation.Tiny.Variant"
tools:lines="2"
tools:text="@tools:sample/lorem/random" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</layout>

View File

@ -1,96 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="item"
type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Selector" />
<variable
name="callback"
type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Callback" />
</data>
<com.google.android.material.card.MaterialCardView
style="@style/WidgetFoundation.Card"
isEnabled="@{item.enabled}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="@{item.enabled ? 1f : .5f}"
android:onClick="@{(view) -> item.onPressed(view, callback)}"
tools:layout_gravity="center">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<ImageView
android:id="@+id/selector_icon"
style="@style/WidgetFoundation.Icon"
gone="@{item.icon == 0}"
android:background="@null"
android:rotation="180"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@{item.icon}"
tools:srcCompat="@drawable/ic_fingerprint" />
<ImageView
android:id="@+id/selector_indicator"
style="@style/WidgetFoundation.Icon"
android:background="@null"
android:rotation="180"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_back_md2" />
<LinearLayout
android:id="@+id/selector_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="@{item.icon == 0 ? (int) @dimen/l1 : 0}"
android:gravity="center_vertical"
android:orientation="vertical"
android:paddingTop="@dimen/l1"
android:paddingBottom="@dimen/l1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/selector_indicator"
app:layout_constraintStart_toEndOf="@+id/selector_icon"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/selector_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="start"
android:text="@{item.title}"
android:textAppearance="@style/AppearanceFoundation.Body"
android:textStyle="bold"
tools:lines="1"
tools:text="@tools:sample/lorem/random" />
<TextView
android:id="@+id/selector_description"
gone="@{item.description.empty}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@{item.description}"
android:textAppearance="@style/AppearanceFoundation.Tiny.Variant"
tools:lines="2"
tools:text="@tools:sample/lorem/random" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</layout>