Make SettingsItems make much more sense

This commit is contained in:
topjohnwu 2020-07-17 03:02:58 -07:00
parent 113eec59f9
commit 4c2570628d
3 changed files with 86 additions and 107 deletions

View File

@ -2,13 +2,10 @@ package com.topjohnwu.magisk.model.entity.recycler
import android.content.Context
import android.content.res.Resources
import android.view.MotionEvent
import android.view.View
import androidx.annotation.ArrayRes
import androidx.annotation.CallSuper
import androidx.databinding.Bindable
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.StaggeredGridLayoutManager
import com.topjohnwu.magisk.BR
import com.topjohnwu.magisk.R
import com.topjohnwu.magisk.databinding.ObservableItem
@ -30,30 +27,19 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
var isEnabled = true
set(value) = set(value, field, { field = it }, BR.enabled)
protected open val isFullSpan get() = false
@CallSuper
open fun onPressed(view: View, callback: Callback) {
callback.onItemChanged(view, this)
callback.onItemPressed(view, this)
}
open fun refresh() {}
override fun onBindingBound(binding: ViewDataBinding) {
super.onBindingBound(binding)
if (isFullSpan) {
val params = binding.root.layoutParams as? StaggeredGridLayoutManager.LayoutParams
params?.isFullSpan = true
}
}
override fun itemSameAs(other: SettingsItem) = this === other
override fun contentSameAs(other: SettingsItem) = itemSameAs(other)
// ---
interface Callback {
fun onItemPressed(view: View, item: SettingsItem, method: () -> Unit)
fun onItemPressed(view: View, item: SettingsItem, callback: () -> Unit = {})
fun onItemChanged(view: View, item: SettingsItem)
}
@ -64,14 +50,38 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
@get:Bindable
abstract var value: T
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)
protected inline fun <reified T> setV(
new: T, old: T, setter: (T) -> Unit, vararg fieldIds: Int) {
set(new, old, setter, BR.value, *fieldIds)
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(
new: T, old: T, setter: (T) -> Unit, afterChanged: (T) -> Unit = {}) {
set(new, old, setter, BR.value, afterChanged = afterChanged)
set(new, old, setter, BR.value) {
afterChanged(it)
callbackVars?.let { pair ->
callbackVars = null
pair.second.onItemChanged(pair.first, this)
}
}
}
}
@ -79,20 +89,9 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
override val layoutRes = R.layout.item_settings_toggle
override fun onPressed(view: View, callback: Callback) {
callback.onItemPressed(view, this) {
value = !value
super.onPressed(view, callback)
}
override fun onPressed(view: View) {
value = !value
}
fun onTouched(view: View, callback: Callback, event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_UP) {
onPressed(view, callback)
}
return true
}
}
abstract class Input : Value<String>(), KoinComponent {
@ -103,29 +102,26 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
protected val resources get() = get<Resources>()
protected abstract val intermediate: String?
override fun onPressed(view: View, callback: Callback) {
callback.onItemPressed(view, this) {
MagiskDialog(view.context)
.applyTitle(title.getText(resources))
.applyView(getView(view.context))
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = android.R.string.ok
onClick {
intermediate?.let { result ->
preventDismiss = false
value = result
it.dismiss()
super.onPressed(view, callback)
return@onClick
}
preventDismiss = true
override fun onPressed(view: View) {
MagiskDialog(view.context)
.applyTitle(title.getText(resources))
.applyView(getView(view.context))
.applyButton(MagiskDialog.ButtonType.POSITIVE) {
titleRes = android.R.string.ok
onClick {
intermediate?.let { result ->
preventDismiss = false
value = result
it.dismiss()
return@onClick
}
preventDismiss = true
}
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel
}
.reveal()
}
}
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel
}
.reveal()
}
abstract fun getView(context: Context): View
@ -150,7 +146,7 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
protected inline fun <reified T> setS(
new: T, old: T, setter: (T) -> Unit, afterChanged: (T) -> Unit = {}) {
set(new, old, setter, BR.value, BR.selectedEntry, BR.description, afterChanged = afterChanged)
setV(new, old, setter, BR.selectedEntry, BR.description, afterChanged = afterChanged)
}
private fun Resources.getArrayOrEmpty(id: Int): Array<String> =
@ -158,40 +154,29 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
override fun onPressed(view: View, callback: Callback) {
if (entries.isEmpty() || entryValues.isEmpty()) return
callback.onItemPressed(view, this) {
MagiskDialog(view.context)
.applyTitle(title.getText(resources))
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel
}
.applyAdapter(entries) {
value = it
notifyPropertyChanged(BR.selectedEntry)
super.onPressed(view, callback)
}
.reveal()
}
super.onPressed(view, callback)
}
override fun onPressed(view: View) {
MagiskDialog(view.context)
.applyTitle(title.getText(resources))
.applyButton(MagiskDialog.ButtonType.NEGATIVE) {
titleRes = android.R.string.cancel
}
.applyAdapter(entries) {
value = it
}
.reveal()
}
}
abstract class Blank : SettingsItem() {
override val layoutRes = R.layout.item_settings_blank
override fun onPressed(view: View, callback: Callback) {
callback.onItemPressed(view, this) {
super.onPressed(view, callback)
}
}
}
abstract class Section : SettingsItem() {
override val layoutRes = R.layout.item_settings_section
override val isFullSpan get() = true
}
}

View File

@ -33,6 +33,12 @@ class SettingsViewModel(
val itemBinding = itemBindingOf<SettingsItem> { it.bindExtra(BR.callback, this) }
val items = diffListOf(createItems())
init {
viewModelScope.launch {
Language.loadLanguages(this)
}
}
private fun createItems(): List<SettingsItem> {
// Customization
val list = mutableListOf(
@ -44,9 +50,6 @@ class SettingsViewModel(
// making theming a pain in the ass. Just forget about it
list.remove(Theme)
}
viewModelScope.launch {
Language.loadLanguages(this)
}
// Manager
list.addAll(listOf(
@ -87,39 +90,34 @@ class SettingsViewModel(
return list
}
override fun onItemPressed(view: View, item: SettingsItem, method: () -> Unit) = when (item) {
is DownloadPath -> withExternalRW(method)
else -> method()
override fun onItemPressed(view: View, item: SettingsItem, callback: () -> Unit) = when (item) {
is DownloadPath -> withExternalRW(callback)
is Biometrics -> authenticate(callback)
is Theme -> SettingsFragmentDirections.actionSettingsFragmentToThemeFragment().publish()
is ClearRepoCache -> clearRepoCache()
is SystemlessHosts -> createHosts()
is Restore -> restoreManager()
else -> callback()
}
override fun onItemChanged(view: View, item: SettingsItem) = when (item) {
// use only instances you want, don't declare everything
is Theme -> SettingsFragmentDirections.actionSettingsFragmentToThemeFragment().publish()
is Language -> RecreateEvent().publish()
is UpdateChannel -> openUrlIfNecessary(view)
is Biometrics -> authenticateOrRevert()
is ClearRepoCache -> clearRepoCache()
is SystemlessHosts -> createHosts()
is Hide -> updateManager(hide = true)
is Restore -> updateManager(hide = false)
is Hide -> PatchAPK.hideManager(view.context, item.value)
else -> Unit
}
private fun openUrlIfNecessary(view: View) {
UpdateChannelUrl.refresh()
if (UpdateChannelUrl.isEnabled && UpdateChannelUrl.value.isBlank()) {
UpdateChannelUrl.onPressed(view, this@SettingsViewModel)
UpdateChannelUrl.onPressed(view, this)
}
}
private fun authenticateOrRevert() {
// immediately revert the preference
Biometrics.value = !Biometrics.value
private fun authenticate(callback: () -> Unit) {
BiometricDialog {
// allow the change on success
onSuccess { Biometrics.value = !Biometrics.value }
onSuccess { callback() }
}.publish()
}
@ -136,13 +134,9 @@ class SettingsViewModel(
}
}
private fun updateManager(hide: Boolean) {
if (hide) {
PatchAPK.hideManager(get(), Hide.value)
} else {
DownloadService(get()) {
subject = DownloadSubject.Manager(Configuration.APK.Restore)
}
private fun restoreManager() {
DownloadService(get()) {
subject = DownloadSubject.Manager(Configuration.APK.Restore)
}
}

View File

@ -21,7 +21,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:alpha="@{item.enabled ? 1f : .5f}"
android:onClick="@{(view) -> callback.onItemChanged(view, item)}"
android:onClick="@{(view) -> item.onPressed(view, callback)}"
tools:layout_gravity="center">
<androidx.constraintlayout.widget.ConstraintLayout
@ -93,4 +93,4 @@
</com.google.android.material.card.MaterialCardView>
</layout>
</layout>