From b95cf9b9a34a281b99822f9662d48b99121cb5c3 Mon Sep 17 00:00:00 2001 From: topjohnwu Date: Fri, 17 Jan 2020 17:02:40 +0800 Subject: [PATCH] Show detail descriptions in settings --- .../model/entity/recycler/SettingsItem.kt | 53 +++++++-------- .../magisk/ui/settings/SettingsItems.kt | 66 ++++++++++--------- .../topjohnwu/magisk/utils/TransitiveText.kt | 4 +- 3 files changed, 62 insertions(+), 61 deletions(-) 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 index 41e551d52..a209b1905 100644 --- 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 @@ -19,19 +19,14 @@ 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 + open val icon: Int get() = 0 + open val title: TransitiveText get() = TransitiveText.EMPTY - var isEnabled = true - @Bindable get - set(value) { - field = value - notifyChange(BR.enabled) - } + @get:Bindable + open val description: TransitiveText get() = TransitiveText.EMPTY + + @get:Bindable + var isEnabled by bindable(true, BR.enabled) protected open val isFullSpan: Boolean = false @@ -41,8 +36,6 @@ sealed class SettingsItem : ObservableItem() { // 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) } @@ -59,6 +52,17 @@ sealed class SettingsItem : ObservableItem() { override fun itemSameAs(other: SettingsItem) = this === other override fun contentSameAs(other: SettingsItem) = itemSameAs(other) + protected inline fun bindable( + initialValue: T, + fieldId: Int, + crossinline setter: (T) -> Unit = {} + ) = object : ObservableProperty(initialValue) { + override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) { + setter(newValue) + notifyChange(fieldId) + } + } + // --- interface Callback { @@ -70,18 +74,13 @@ sealed class SettingsItem : ObservableItem() { abstract class Value : SettingsItem() { + @get:Bindable abstract var value: T - @Bindable get - protected inline fun dataObservable( + protected inline fun bindableValue( initialValue: T, crossinline setter: (T) -> Unit - ) = object : ObservableProperty(initialValue) { - override fun afterChange(property: KProperty<*>, oldValue: T, newValue: T) { - setter(newValue) - notifyChange(BR.value) - } - } + ) = bindable(initialValue, BR.value, setter) } @@ -146,15 +145,15 @@ sealed class SettingsItem : ObservableItem() { protected val resources get() = get() - @Bindable var entries: Array = arrayOf() private set - @Bindable + var entryValues: Array = arrayOf() private set + @get:Bindable val selectedEntry - @Bindable get() = entries.getOrNull(value) + get() = entries.getOrNull(value) fun setValues( entries: Array, @@ -165,8 +164,6 @@ sealed class SettingsItem : ObservableItem() { this.entries = entries this.entryValues = values - notifyChange(BR.entries) - notifyChange(BR.entryValues) notifyChange(BR.selectedEntry) } @@ -206,4 +203,4 @@ sealed class SettingsItem : ObservableItem() { } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsItems.kt b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsItems.kt index 31f6c5032..210ab97f8 100644 --- a/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsItems.kt +++ b/app/src/main/java/com/topjohnwu/magisk/ui/settings/SettingsItems.kt @@ -12,17 +12,13 @@ import com.topjohnwu.magisk.R import com.topjohnwu.magisk.core.Config import com.topjohnwu.magisk.core.Const import com.topjohnwu.magisk.core.Info -import com.topjohnwu.magisk.core.utils.availableLocales -import com.topjohnwu.magisk.core.utils.currentLocale -import com.topjohnwu.magisk.core.utils.refreshLocale +import com.topjohnwu.magisk.core.utils.* import com.topjohnwu.magisk.databinding.DialogSettingsAppNameBinding 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.core.utils.BiometricHelper -import com.topjohnwu.magisk.core.utils.Utils import com.topjohnwu.magisk.utils.asTransitive import com.topjohnwu.superuser.Shell import java.io.File @@ -36,8 +32,8 @@ object Customization : SettingsItem.Section() { } object Language : SettingsItem.Selector() { - override var value by dataObservable(0) { - Config.locale = entryValues.getOrNull(it)?.toString() ?: return@dataObservable + override var value by bindableValue(0) { + Config.locale = entryValues.getOrNull(it)?.toString() ?: return@bindableValue refreshLocale() } @@ -108,7 +104,7 @@ fun HideOrRestore() = if (get().packageName == BuildConfig.APPLICATION_ID) Hide else Restore object DownloadPath : SettingsItem.Input() { - override var value: String by dataObservable(Config.downloadPath) { Config.downloadPath = it } + override var value: String by bindableValue(Config.downloadPath) { Config.downloadPath = it } override val title = R.string.settings_download_path_title.asTransitive() override val intermediate: String? get() = if (Utils.ensureDownloadPath(result) != null) result else null @@ -130,7 +126,7 @@ object DownloadPath : SettingsItem.Input() { } object GridSize : SettingsItem.Selector() { - override var value by dataObservable(Config.listSpanCount - 1) { + override var value by bindableValue(Config.listSpanCount - 1) { Config.listSpanCount = max(1, min(3, it + 1)) } @@ -146,7 +142,7 @@ object GridSize : SettingsItem.Selector() { } object UpdateChannel : SettingsItem.Selector() { - override var value by dataObservable(Config.updateChannel) { Config.updateChannel = it } + override var value by bindableValue(Config.updateChannel) { Config.updateChannel = it } override val title = R.string.settings_update_channel_title.asTransitive() init { @@ -163,7 +159,7 @@ object UpdateChannel : SettingsItem.Selector() { object UpdateChannelUrl : SettingsItem.Input() { override val title = R.string.settings_update_custom.asTransitive() - override var value: String by dataObservable(Config.customChannelUrl) { + override var value: String by bindableValue(Config.customChannelUrl) { Config.customChannelUrl = it } override val intermediate: String? get() = result @@ -186,7 +182,7 @@ object UpdateChannelUrl : SettingsItem.Input() { object UpdateChecker : SettingsItem.Toggle() { override val title = R.string.settings_check_update_title.asTransitive() override val description = R.string.settings_check_update_summary.asTransitive() - override var value by dataObservable(Config.checkUpdate) { + override var value by bindableValue(Config.checkUpdate) { Config.checkUpdate = it Utils.scheduleUpdateCheck(get()) } @@ -204,13 +200,14 @@ object SystemlessHosts : SettingsItem.Blank() { object Biometrics : SettingsItem.Toggle() { override val title = R.string.settings_su_biometric_title.asTransitive() - override val description = R.string.settings_su_biometric_summary.asTransitive() - override var value by dataObservable(Config.suBiometric) { Config.suBiometric = it } + override var value by bindableValue(Config.suBiometric) { Config.suBiometric = it } + override var description = R.string.settings_su_biometric_summary.asTransitive() override fun refresh() { isEnabled = BiometricHelper.isSupported && Utils.showSuperUser() if (!isEnabled) { value = false + description = R.string.no_biometric.asTransitive() } } } @@ -218,7 +215,7 @@ object Biometrics : SettingsItem.Toggle() { object Reauthenticate : SettingsItem.Toggle() { override val title = R.string.settings_su_reauth_title.asTransitive() override val description = R.string.settings_su_reauth_summary.asTransitive() - override var value by dataObservable(Config.suReAuth) { Config.suReAuth = it } + override var value by bindableValue(Config.suReAuth) { Config.suReAuth = it } override fun refresh() { isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O && Utils.showSuperUser() @@ -233,9 +230,10 @@ object Magisk : SettingsItem.Section() { object SafeMode : SettingsItem.Toggle() { override val title = R.string.settings_safe_mode_title.asTransitive() - override val description = R.string.settings_safe_mode_summary.asTransitive() - override var value by dataObservable(Config.coreOnly) { - if (Config.coreOnly == it) return@dataObservable + // Use old placeholder for now, will update text once native implementation is changed + override val description = R.string.settings_core_only_summary.asTransitive() + override var value by bindableValue(Config.coreOnly) { + if (Config.coreOnly == it) return@bindableValue Config.coreOnly = it when { it -> runCatching { Const.MAGISK_DISABLE_FILE.createNewFile() } @@ -252,7 +250,7 @@ object SafeMode : SettingsItem.Toggle() { object MagiskHide : SettingsItem.Toggle() { override val title = R.string.magiskhide.asTransitive() override val description = R.string.settings_magiskhide_summary.asTransitive() - override var value by dataObservable(Config.magiskHide) { + override var value by bindableValue(Config.magiskHide) { Config.magiskHide = it when { it -> Shell.su("magiskhide --enable").submit() @@ -273,10 +271,10 @@ object Superuser : SettingsItem.Section() { object AccessMode : SettingsItem.Selector() { override val title = R.string.superuser_access.asTransitive() - override var value by dataObservable(Config.rootMode) { + override var value by bindableValue(Config.rootMode) { Config.rootMode = entryValues.getOrNull(it) ?.toString() - ?.toInt() ?: return@dataObservable + ?.toInt() ?: return@bindableValue } init { @@ -293,11 +291,14 @@ object AccessMode : SettingsItem.Selector() { object MultiuserMode : SettingsItem.Selector() { override val title = R.string.multiuser_mode.asTransitive() - override var value by dataObservable(Config.suMultiuserMode) { + override var value by bindableValue(Config.suMultiuserMode) { Config.suMultiuserMode = entryValues.getOrNull(it) ?.toString() - ?.toInt() ?: return@dataObservable + ?.toInt() ?: return@bindableValue } + private val descArray = resources.getStringArray(R.array.multiuser_summary) + override val description + get() = descArray[value].asTransitive() init { setValues( @@ -313,11 +314,14 @@ object MultiuserMode : SettingsItem.Selector() { object MountNamespaceMode : SettingsItem.Selector() { override val title = R.string.mount_namespace_mode.asTransitive() - override var value by dataObservable(Config.suMntNamespaceMode) { + override var value by bindableValue(Config.suMntNamespaceMode) { Config.suMntNamespaceMode = entryValues.getOrNull(it) ?.toString() - ?.toInt() ?: return@dataObservable + ?.toInt() ?: return@bindableValue } + private val descArray = resources.getStringArray(R.array.namespace_summary) + override val description + get() = descArray[value].asTransitive() init { setValues( @@ -333,10 +337,10 @@ object MountNamespaceMode : SettingsItem.Selector() { object AutomaticResponse : SettingsItem.Selector() { override val title = R.string.auto_response.asTransitive() - override var value by dataObservable(Config.suAutoReponse) { + override var value by bindableValue(Config.suAutoReponse) { Config.suAutoReponse = entryValues.getOrNull(it) ?.toString() - ?.toInt() ?: return@dataObservable + ?.toInt() ?: return@bindableValue } init { @@ -353,10 +357,10 @@ object AutomaticResponse : SettingsItem.Selector() { object RequestTimeout : SettingsItem.Selector() { override val title = R.string.request_timeout.asTransitive() - override var value by dataObservable(-1) { + override var value by bindableValue(-1) { Config.suDefaultTimeout = entryValues.getOrNull(it) ?.toString() - ?.toInt() ?: return@dataObservable + ?.toInt() ?: return@bindableValue } init { @@ -375,10 +379,10 @@ object RequestTimeout : SettingsItem.Selector() { object SUNotification : SettingsItem.Selector() { override val title = R.string.superuser_notification.asTransitive() - override var value by dataObservable(Config.suNotification) { + override var value by bindableValue(Config.suNotification) { Config.suNotification = entryValues.getOrNull(it) ?.toString() - ?.toInt() ?: return@dataObservable + ?.toInt() ?: return@bindableValue } init { diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/TransitiveText.kt b/app/src/main/java/com/topjohnwu/magisk/utils/TransitiveText.kt index f901a5889..9ff4efa6a 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/TransitiveText.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/TransitiveText.kt @@ -35,7 +35,7 @@ sealed class TransitiveText { // --- companion object { - val empty = String("") + val EMPTY = String("") } } @@ -51,4 +51,4 @@ fun TextView.setText(text: TransitiveText) { } @InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged") -fun TextView.getTransitiveText() = text.asTransitive() \ No newline at end of file +fun TextView.getTransitiveText() = text.asTransitive()