diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/ModuleRvItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/ModuleRvItem.kt index 5e170bc8b..b748ffc94 100644 --- a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/ModuleRvItem.kt +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/ModuleRvItem.kt @@ -218,6 +218,6 @@ abstract class ObservableItem : ComparableRvItem(), Observable { list.add(callback ?: return) } - protected fun notifyChange(id: Int) = list.notifyChange(this, id) + fun notifyChange(id: Int) = list.notifyChange(this, id) } \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SettingsItem.kt b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SettingsItem.kt new file mode 100644 index 000000000..41e551d52 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/model/entity/recycler/SettingsItem.kt @@ -0,0 +1,209 @@ +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.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.utils.TransitiveText +import com.topjohnwu.magisk.view.MagiskDialog +import org.koin.core.KoinComponent +import org.koin.core.get +import kotlin.properties.ObservableProperty +import kotlin.reflect.KProperty + +sealed class SettingsItem : ObservableItem() { + + @Bindable + open val icon: Int = 0 + @Bindable + open val title: TransitiveText = TransitiveText.empty + @Bindable + open val description: TransitiveText = TransitiveText.empty + + var isEnabled = true + @Bindable get + set(value) { + field = value + notifyChange(BR.enabled) + } + + protected open val isFullSpan: Boolean = false + + @CallSuper + open fun onPressed(view: View, callback: Callback) { + callback.onItemChanged(view, this) + + // notify only after the callback invocation; callback can invalidate the backing data, + // which wouldn't be recognized with reverse approach + notifyChange(BR.icon) + notifyChange(BR.title) + notifyChange(BR.description) + } + + 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) + fun onItemChanged(view: View, item: SettingsItem) + } + + // --- + + abstract class Value : SettingsItem() { + + abstract var value: T + @Bindable get + + protected inline fun dataObservable( + initialValue: T, + crossinline setter: (T) -> Unit + ) = object : ObservableProperty(initialValue) { + override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) { + setter(newValue) + notifyChange(BR.value) + } + } + + } + + abstract class Toggle : Value() { + + 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) + } + + fun onTouched(view: View, callback: Callback, event: MotionEvent): Boolean { + if (event.action == MotionEvent.ACTION_UP) { + onPressed(view, callback) + } + return true + } + + } + + abstract class Input : Value(), KoinComponent { + + override val layoutRes = R.layout.item_settings_input + open val showStrip = true + + protected val resources get() = get() + 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 + } + } + .applyButton(MagiskDialog.ButtonType.NEGATIVE) { + titleRes = android.R.string.cancel + } + .reveal() + } + + abstract fun getView(context: Context): View + + } + + abstract class Selector : Value(), KoinComponent { + + override val layoutRes = R.layout.item_settings_selector + + protected val resources get() = get() + + @Bindable + var entries: Array = arrayOf() + private set + @Bindable + var entryValues: Array = arrayOf() + private set + + val selectedEntry + @Bindable get() = entries.getOrNull(value) + + fun setValues( + entries: Array, + values: Array + ) { + check(entries.size <= values.size) { "List sizes must match" } + + this.entries = entries + this.entryValues = values + + notifyChange(BR.entries) + notifyChange(BR.entryValues) + notifyChange(BR.selectedEntry) + } + + 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 + notifyChange(BR.selectedEntry) + super.onPressed(view, callback) + } + .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 = true + + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsItems.kt b/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsItems.kt index 8bd5b4fcf..882ee4262 100644 --- a/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsItems.kt +++ b/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsItems.kt @@ -12,6 +12,7 @@ import com.topjohnwu.magisk.databinding.DialogSettingsDownloadPathBinding import com.topjohnwu.magisk.databinding.DialogSettingsUpdateChannelBinding import com.topjohnwu.magisk.extensions.get import com.topjohnwu.magisk.extensions.subscribeK +import com.topjohnwu.magisk.model.entity.recycler.SettingsItem import com.topjohnwu.magisk.utils.* import com.topjohnwu.superuser.Shell import java.io.File diff --git a/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsViewModel.kt b/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsViewModel.kt index d3773b0f3..58faf4df2 100644 --- a/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsViewModel.kt +++ b/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsViewModel.kt @@ -1,15 +1,8 @@ package com.topjohnwu.magisk.redesign.settings import android.Manifest -import android.content.Context -import android.content.res.Resources -import android.view.MotionEvent import android.view.View import android.widget.Toast -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.data.database.RepoDao @@ -17,7 +10,7 @@ import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.model.download.DownloadService import com.topjohnwu.magisk.model.entity.internal.Configuration import com.topjohnwu.magisk.model.entity.internal.DownloadSubject -import com.topjohnwu.magisk.model.entity.recycler.ObservableItem +import com.topjohnwu.magisk.model.entity.recycler.SettingsItem import com.topjohnwu.magisk.model.events.DieEvent import com.topjohnwu.magisk.model.events.PermissionEvent import com.topjohnwu.magisk.model.events.RecreateEvent @@ -28,16 +21,11 @@ import com.topjohnwu.magisk.redesign.home.itemBindingOf import com.topjohnwu.magisk.redesign.module.adapterOf import com.topjohnwu.magisk.redesign.superuser.diffListOf import com.topjohnwu.magisk.utils.PatchAPK -import com.topjohnwu.magisk.utils.TransitiveText import com.topjohnwu.magisk.utils.Utils -import com.topjohnwu.magisk.view.MagiskDialog import com.topjohnwu.superuser.Shell import io.reactivex.Completable import io.reactivex.subjects.PublishSubject -import org.koin.core.KoinComponent import org.koin.core.get -import kotlin.properties.ObservableProperty -import kotlin.reflect.KProperty class SettingsViewModel( private val repositoryDao: RepoDao @@ -130,195 +118,4 @@ class SettingsViewModel( } } -} - -sealed class SettingsItem : ObservableItem() { - - @Bindable - open val icon: Int = 0 - @Bindable - open val title: TransitiveText = TransitiveText.empty - @Bindable - open val description: TransitiveText = TransitiveText.empty - - var isEnabled = true - @Bindable get - set(value) { - field = value - notifyChange(BR.enabled) - } - - protected open val isFullSpan: Boolean = false - - @CallSuper - open fun onPressed(view: View, callback: Callback) { - callback.onItemChanged(view, this) - - // notify only after the callback invocation; callback can invalidate the backing data, - // which wouldn't be recognized with reverse approach - notifyChange(BR.icon) - notifyChange(BR.title) - notifyChange(BR.description) - } - - 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) - fun onItemChanged(view: View, item: SettingsItem) - } - - // --- - - abstract class Value : SettingsItem() { - - abstract var value: T - @Bindable get - - protected inline fun dataObservable( - initialValue: T, - crossinline setter: (T) -> Unit - ) = object : ObservableProperty(initialValue) { - override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) { - setter(newValue) - notifyChange(BR.value) - } - } - - } - - abstract class Toggle : Value() { - - 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) - } - - fun onTouched(view: View, callback: Callback, event: MotionEvent): Boolean { - if (event.action == MotionEvent.ACTION_UP) { - onPressed(view, callback) - } - return true - } - - } - - abstract class Input : Value(), KoinComponent { - - override val layoutRes = R.layout.item_settings_input - open val showStrip = true - - protected val resources get() = get() - 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 - } - } - .applyButton(MagiskDialog.ButtonType.NEGATIVE) { - titleRes = android.R.string.cancel - } - .reveal() - } - - abstract fun getView(context: Context): View - - } - - abstract class Selector : Value(), KoinComponent { - - override val layoutRes = R.layout.item_settings_selector - - protected val resources get() = get() - - @Bindable - var entries: Array = arrayOf() - private set - @Bindable - var entryValues: Array = arrayOf() - private set - - val selectedEntry - @Bindable get() = entries.getOrNull(value) - - fun setValues( - entries: Array, - values: Array - ) { - check(entries.size <= values.size) { "List sizes must match" } - - this.entries = entries - this.entryValues = values - - notifyChange(BR.entries) - notifyChange(BR.entryValues) - notifyChange(BR.selectedEntry) - } - - 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 - notifyChange(BR.selectedEntry) - super.onPressed(view, callback) - } - .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 = true - - } - } \ No newline at end of file diff --git a/app/src/main/res/layout/item_settings_blank.xml b/app/src/main/res/layout/item_settings_blank.xml index a6df8aad7..31de4e803 100644 --- a/app/src/main/res/layout/item_settings_blank.xml +++ b/app/src/main/res/layout/item_settings_blank.xml @@ -7,11 +7,11 @@ + type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Blank" /> + type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Callback" /> diff --git a/app/src/main/res/layout/item_settings_input.xml b/app/src/main/res/layout/item_settings_input.xml index 64aac7865..0eb5fac97 100644 --- a/app/src/main/res/layout/item_settings_input.xml +++ b/app/src/main/res/layout/item_settings_input.xml @@ -7,11 +7,11 @@ + type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Input" /> + type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Callback" /> diff --git a/app/src/main/res/layout/item_settings_section.xml b/app/src/main/res/layout/item_settings_section.xml index 7c6a29e10..92e751ee6 100644 --- a/app/src/main/res/layout/item_settings_section.xml +++ b/app/src/main/res/layout/item_settings_section.xml @@ -6,7 +6,7 @@ + type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem" /> diff --git a/app/src/main/res/layout/item_settings_selector.xml b/app/src/main/res/layout/item_settings_selector.xml index 66be0bcc3..edebf5b91 100644 --- a/app/src/main/res/layout/item_settings_selector.xml +++ b/app/src/main/res/layout/item_settings_selector.xml @@ -7,11 +7,11 @@ + type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Selector" /> + type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Callback" /> diff --git a/app/src/main/res/layout/item_settings_toggle.xml b/app/src/main/res/layout/item_settings_toggle.xml index 171bd267d..14b6e3749 100644 --- a/app/src/main/res/layout/item_settings_toggle.xml +++ b/app/src/main/res/layout/item_settings_toggle.xml @@ -9,11 +9,11 @@ + type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Toggle" /> + type="com.topjohnwu.magisk.model.entity.recycler.SettingsItem.Callback" />