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.Context
import android.content.res.Resources import android.content.res.Resources
import android.view.MotionEvent
import android.view.View import android.view.View
import androidx.annotation.ArrayRes import androidx.annotation.ArrayRes
import androidx.annotation.CallSuper import androidx.annotation.CallSuper
import androidx.databinding.Bindable import androidx.databinding.Bindable
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.StaggeredGridLayoutManager
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.ObservableItem import com.topjohnwu.magisk.databinding.ObservableItem
@ -30,30 +27,19 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
var isEnabled = true var isEnabled = true
set(value) = set(value, field, { field = it }, BR.enabled) set(value) = set(value, field, { field = it }, BR.enabled)
protected open val isFullSpan get() = false
@CallSuper
open fun onPressed(view: View, callback: Callback) { open fun onPressed(view: View, callback: Callback) {
callback.onItemChanged(view, this) callback.onItemPressed(view, this)
} }
open fun refresh() {} 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 itemSameAs(other: SettingsItem) = this === other
override fun contentSameAs(other: SettingsItem) = itemSameAs(other) override fun contentSameAs(other: SettingsItem) = itemSameAs(other)
// --- // ---
interface Callback { interface Callback {
fun onItemPressed(view: View, item: SettingsItem, method: () -> Unit) fun onItemPressed(view: View, item: SettingsItem, callback: () -> Unit = {})
fun onItemChanged(view: View, item: SettingsItem) fun onItemChanged(view: View, item: SettingsItem)
} }
@ -64,14 +50,38 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
@get:Bindable @get:Bindable
abstract var value: T 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( protected inline fun <reified T> setV(
new: T, old: T, setter: (T) -> Unit, vararg fieldIds: Int) { new: T, old: T, setter: (T) -> Unit, vararg fieldIds: Int, afterChanged: (T) -> Unit = {}) {
set(new, old, setter, BR.value, *fieldIds) 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, 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 val layoutRes = R.layout.item_settings_toggle
override fun onPressed(view: View, callback: Callback) { override fun onPressed(view: View) {
callback.onItemPressed(view, this) { value = !value
value = !value
super.onPressed(view, callback)
}
} }
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 { abstract class Input : Value<String>(), KoinComponent {
@ -103,29 +102,26 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
protected val resources get() = get<Resources>() protected val resources get() = get<Resources>()
protected abstract val intermediate: String? protected abstract val intermediate: String?
override fun onPressed(view: View, callback: Callback) { override fun onPressed(view: View) {
callback.onItemPressed(view, this) { MagiskDialog(view.context)
MagiskDialog(view.context) .applyTitle(title.getText(resources))
.applyTitle(title.getText(resources)) .applyView(getView(view.context))
.applyView(getView(view.context)) .applyButton(MagiskDialog.ButtonType.POSITIVE) {
.applyButton(MagiskDialog.ButtonType.POSITIVE) { titleRes = android.R.string.ok
titleRes = android.R.string.ok onClick {
onClick { intermediate?.let { result ->
intermediate?.let { result -> preventDismiss = false
preventDismiss = false value = result
value = result it.dismiss()
it.dismiss() return@onClick
super.onPressed(view, callback)
return@onClick
}
preventDismiss = true
} }
preventDismiss = true
} }
.applyButton(MagiskDialog.ButtonType.NEGATIVE) { }
titleRes = android.R.string.cancel .applyButton(MagiskDialog.ButtonType.NEGATIVE) {
} titleRes = android.R.string.cancel
.reveal() }
} .reveal()
} }
abstract fun getView(context: Context): View abstract fun getView(context: Context): View
@ -150,7 +146,7 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
protected inline fun <reified T> setS( protected inline fun <reified T> setS(
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, BR.selectedEntry, BR.description, afterChanged = afterChanged) setV(new, old, setter, BR.selectedEntry, BR.description, afterChanged = afterChanged)
} }
private fun Resources.getArrayOrEmpty(id: Int): Array<String> = private fun Resources.getArrayOrEmpty(id: Int): Array<String> =
@ -158,40 +154,29 @@ sealed class SettingsItem : ObservableItem<SettingsItem>() {
override fun onPressed(view: View, callback: Callback) { override fun onPressed(view: View, callback: Callback) {
if (entries.isEmpty() || entryValues.isEmpty()) return if (entries.isEmpty() || entryValues.isEmpty()) return
callback.onItemPressed(view, this) { super.onPressed(view, callback)
MagiskDialog(view.context) }
.applyTitle(title.getText(resources))
.applyButton(MagiskDialog.ButtonType.NEGATIVE) { override fun onPressed(view: View) {
titleRes = android.R.string.cancel MagiskDialog(view.context)
} .applyTitle(title.getText(resources))
.applyAdapter(entries) { .applyButton(MagiskDialog.ButtonType.NEGATIVE) {
value = it titleRes = android.R.string.cancel
notifyPropertyChanged(BR.selectedEntry) }
super.onPressed(view, callback) .applyAdapter(entries) {
} value = it
.reveal() }
} .reveal()
} }
} }
abstract class Blank : SettingsItem() { abstract class Blank : SettingsItem() {
override val layoutRes = R.layout.item_settings_blank 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() { abstract class Section : SettingsItem() {
override val layoutRes = R.layout.item_settings_section 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 itemBinding = itemBindingOf<SettingsItem> { it.bindExtra(BR.callback, this) }
val items = diffListOf(createItems()) val items = diffListOf(createItems())
init {
viewModelScope.launch {
Language.loadLanguages(this)
}
}
private fun createItems(): List<SettingsItem> { private fun createItems(): List<SettingsItem> {
// Customization // Customization
val list = mutableListOf( val list = mutableListOf(
@ -44,9 +50,6 @@ class SettingsViewModel(
// making theming a pain in the ass. Just forget about it // making theming a pain in the ass. Just forget about it
list.remove(Theme) list.remove(Theme)
} }
viewModelScope.launch {
Language.loadLanguages(this)
}
// Manager // Manager
list.addAll(listOf( list.addAll(listOf(
@ -87,39 +90,34 @@ class SettingsViewModel(
return list return list
} }
override fun onItemPressed(view: View, item: SettingsItem, method: () -> Unit) = when (item) { override fun onItemPressed(view: View, item: SettingsItem, callback: () -> Unit) = when (item) {
is DownloadPath -> withExternalRW(method) is DownloadPath -> withExternalRW(callback)
else -> method() 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) { 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 Language -> RecreateEvent().publish()
is UpdateChannel -> openUrlIfNecessary(view) is UpdateChannel -> openUrlIfNecessary(view)
is Biometrics -> authenticateOrRevert() is Hide -> PatchAPK.hideManager(view.context, item.value)
is ClearRepoCache -> clearRepoCache()
is SystemlessHosts -> createHosts()
is Hide -> updateManager(hide = true)
is Restore -> updateManager(hide = false)
else -> Unit else -> Unit
} }
private fun openUrlIfNecessary(view: View) { private fun openUrlIfNecessary(view: View) {
UpdateChannelUrl.refresh() UpdateChannelUrl.refresh()
if (UpdateChannelUrl.isEnabled && UpdateChannelUrl.value.isBlank()) { if (UpdateChannelUrl.isEnabled && UpdateChannelUrl.value.isBlank()) {
UpdateChannelUrl.onPressed(view, this@SettingsViewModel) UpdateChannelUrl.onPressed(view, this)
} }
} }
private fun authenticateOrRevert() { private fun authenticate(callback: () -> Unit) {
// immediately revert the preference
Biometrics.value = !Biometrics.value
BiometricDialog { BiometricDialog {
// allow the change on success // allow the change on success
onSuccess { Biometrics.value = !Biometrics.value } onSuccess { callback() }
}.publish() }.publish()
} }
@ -136,13 +134,9 @@ class SettingsViewModel(
} }
} }
private fun updateManager(hide: Boolean) { private fun restoreManager() {
if (hide) { DownloadService(get()) {
PatchAPK.hideManager(get(), Hide.value) subject = DownloadSubject.Manager(Configuration.APK.Restore)
} else {
DownloadService(get()) {
subject = DownloadSubject.Manager(Configuration.APK.Restore)
}
} }
} }

View File

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