From cb96b536a2e984859eca111a1f3c4c361ba8ebea Mon Sep 17 00:00:00 2001 From: Viktor De Pasquale Date: Thu, 28 Nov 2019 21:53:31 +0100 Subject: [PATCH] Added fair amount of settings implemented from the UI side Updated dialog to create recycler as it behaves better than regular listview --- .../magisk/redesign/settings/SettingsItems.kt | 134 ++++++++++++++++-- .../redesign/settings/SettingsViewModel.kt | 65 +++++++-- .../com/topjohnwu/magisk/view/MagiskDialog.kt | 63 ++++++-- .../main/res/layout/dialog_magisk_base.xml | 2 +- .../main/res/layout/item_list_single_line.xml | 31 ++++ .../res/layout/item_settings_selector.xml | 19 ++- 6 files changed, 278 insertions(+), 36 deletions(-) create mode 100644 app/src/main/res/layout/item_list_single_line.xml 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 4ebb43f6a..2fceed1d1 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 @@ -5,7 +5,10 @@ import com.topjohnwu.magisk.BuildConfig import com.topjohnwu.magisk.Config import com.topjohnwu.magisk.R import com.topjohnwu.magisk.extensions.get +import com.topjohnwu.magisk.extensions.subscribeK import com.topjohnwu.magisk.utils.asTransitive +import com.topjohnwu.magisk.utils.availableLocales +import com.topjohnwu.magisk.utils.currentLocale // --- Customization @@ -13,6 +16,22 @@ object Customization : SettingsItem.Section() { override val title = "Customization".asTransitive() } +object Language : SettingsItem.Selector() { + override var value by dataObservable(0) { + Config.locale = entryValues.getOrNull(it)?.toString() ?: return@dataObservable + } + + override val title = R.string.language.asTransitive() + + init { + availableLocales.subscribeK { (names, values) -> + setValues(names, values) + val selectedLocale = currentLocale.getDisplayName(currentLocale) + value = names.indexOfFirst { it == selectedLocale }.let { if (it == -1) 0 else it } + } + } +} + object Redesign : SettingsItem.Toggle() { override val title = "Redesign".asTransitive() override val description = @@ -31,7 +50,6 @@ object Manager : SettingsItem.Section() { override val title = R.string.manager.asTransitive() } -object Language object ClearRepoCache : SettingsItem.Blank() { override val title = R.string.settings_clear_cache_title.asTransitive() override val description = R.string.settings_clear_cache_summary.asTransitive() @@ -51,8 +69,17 @@ object Restore : SettingsItem.Blank() { fun HideOrRestore() = if (get().packageName == BuildConfig.APPLICATION_ID) Hide else Restore +//todo new dialog object DownloadPath -object UpdateChannel + +//fixme this +object UpdateChannel : SettingsItem.Selector() { + override var value by dataObservable(Config.updateChannel) { Config.updateChannel = it } +} + +//fixme new dialog +object UpdateChannelUrl + object UpdateChecker : SettingsItem.Toggle() { override val title = R.string.settings_check_update_title.asTransitive() override val description = R.string.settings_check_update_summary.asTransitive() @@ -95,9 +122,100 @@ object Superuser : SettingsItem.Section() { override val title = R.string.superuser.asTransitive() } -object AccessMode -object MultiuserMode -object MountNamespaceMode -object AutomaticResponse -object RequestTimeout -object SUNotification \ No newline at end of file +object AccessMode : SettingsItem.Selector() { + override val title = R.string.superuser_access.asTransitive() + override var value by dataObservable(Config.rootMode) { + Config.rootMode = entryValues.getOrNull(it) + ?.toString() + ?.toInt() ?: return@dataObservable + } + + init { + setValues( + resources.getStringArray(R.array.su_access), + resources.getStringArray(R.array.value_array) + ) + } +} + +object MultiuserMode : SettingsItem.Selector() { + override val title = R.string.multiuser_mode.asTransitive() + override var value by dataObservable(Config.suMultiuserMode) { + Config.suMultiuserMode = entryValues.getOrNull(it) + ?.toString() + ?.toInt() ?: return@dataObservable + } + + init { + setValues( + resources.getStringArray(R.array.multiuser_mode), + resources.getStringArray(R.array.value_array) + ) + } +} + +object MountNamespaceMode : SettingsItem.Selector() { + override val title = R.string.mount_namespace_mode.asTransitive() + override var value by dataObservable(Config.suMntNamespaceMode) { + Config.suMntNamespaceMode = entryValues.getOrNull(it) + ?.toString() + ?.toInt() ?: return@dataObservable + } + + init { + setValues( + resources.getStringArray(R.array.namespace), + resources.getStringArray(R.array.value_array) + ) + } +} + +object AutomaticResponse : SettingsItem.Selector() { + override val title = R.string.auto_response.asTransitive() + override var value by dataObservable(Config.suAutoReponse) { + Config.suAutoReponse = entryValues.getOrNull(it) + ?.toString() + ?.toInt() ?: return@dataObservable + } + + init { + setValues( + resources.getStringArray(R.array.auto_response), + resources.getStringArray(R.array.value_array) + ) + } +} + +object RequestTimeout : SettingsItem.Selector() { + override val title = R.string.request_timeout.asTransitive() + override var value by dataObservable(-1) { + Config.suDefaultTimeout = entryValues.getOrNull(it) + ?.toString() + ?.toInt() ?: return@dataObservable + } + + init { + setValues( + resources.getStringArray(R.array.request_timeout), + resources.getStringArray(R.array.request_timeout_value) + ) + val currentValue = Config.suDefaultTimeout.toString() + value = entryValues.indexOfFirst { it == currentValue } + } +} + +object SUNotification : SettingsItem.Selector() { + override val title = R.string.superuser_notification.asTransitive() + override var value by dataObservable(Config.suNotification) { + Config.suNotification = entryValues.getOrNull(it) + ?.toString() + ?.toInt() ?: return@dataObservable + } + + init { + setValues( + resources.getStringArray(R.array.su_notification), + resources.getStringArray(R.array.value_array) + ) + } +} \ No newline at end of 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 6a261ec2a..68fd19f13 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,5 +1,6 @@ package com.topjohnwu.magisk.redesign.settings +import android.content.res.Resources import android.view.MotionEvent import android.view.View import androidx.annotation.CallSuper @@ -17,6 +18,8 @@ import com.topjohnwu.magisk.redesign.module.adapterOf import com.topjohnwu.magisk.redesign.superuser.diffListOf 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 @@ -25,15 +28,18 @@ class SettingsViewModel : CompatViewModel(), SettingsItem.Callback { val adapter = adapterOf() val itemBinding = itemBindingOf { it.bindExtra(BR.callback, this) } val items = diffListOf( - // General - // Customization - Customization, Theme, Redesign, - // Manager - Manager, ClearRepoCache, HideOrRestore(), UpdateChecker, SystemlessHosts, Biometrics, - // Magisk - Magisk, SafeMode, MagiskHide, - // Superuser - Superuser + Customization, + Theme, Language, Redesign, + + Manager, + ClearRepoCache, HideOrRestore(), UpdateChecker, SystemlessHosts, Biometrics, + + Magisk, + SafeMode, MagiskHide, + + Superuser, + AccessMode, MultiuserMode, MountNamespaceMode, AutomaticResponse, RequestTimeout, + SUNotification ) override fun onItemPressed(view: View, item: SettingsItem) = when (item) { @@ -121,14 +127,49 @@ sealed class SettingsItem : ObservableItem() { } - abstract class Selector : Value() { + 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 MagiskDialog(view.context) - //.applyAdapter() - super.onPressed(view, callback) + .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() } } diff --git a/app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt b/app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt index f4b698340..661cbfa58 100644 --- a/app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt +++ b/app/src/main/java/com/topjohnwu/magisk/view/MagiskDialog.kt @@ -8,13 +8,22 @@ import android.graphics.drawable.Drawable import android.os.Bundle import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import androidx.annotation.DrawableRes import androidx.annotation.StringRes import androidx.appcompat.app.AlertDialog +import androidx.core.view.updateLayoutParams import androidx.databinding.ViewDataBinding +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView import com.topjohnwu.magisk.BR +import com.topjohnwu.magisk.R +import com.topjohnwu.magisk.databinding.ComparableRvItem import com.topjohnwu.magisk.databinding.DialogMagiskBaseBinding +import com.topjohnwu.magisk.redesign.home.itemBindingOf import com.topjohnwu.magisk.utils.KObservableField +import me.tatarka.bindingcollectionadapter2.BindingRecyclerViewAdapters +import me.tatarka.bindingcollectionadapter2.ItemBinding class MagiskDialog @JvmOverloads constructor( context: Context, theme: Int = 0 @@ -138,20 +147,57 @@ class MagiskDialog @JvmOverloads constructor( ButtonBuilder(button).apply(builder) } + class DialogItem( + val item: CharSequence, + val position: Int + ) : ComparableRvItem() { + override val layoutRes = R.layout.item_list_single_line + override fun itemSameAs(other: DialogItem) = item == other.item + override fun contentSameAs(other: DialogItem) = itemSameAs(other) + } + + interface ActualOnDialogClickListener { + fun onClick(position: Int) + } + + fun applyAdapter( + list: Array, + listener: OnDialogClickListener + ) = applyView( + RecyclerView(context).also { + it.isNestedScrollingEnabled = false + it.layoutManager = LinearLayoutManager(context) + + val actualListener = object : ActualOnDialogClickListener { + override fun onClick(position: Int) { + listener(position) + dismiss() + } + } + val items = list.mapIndexed { i, it -> DialogItem(it, i) } + val binding = itemBindingOf { it.bindExtra(BR.listener, actualListener) } + .let { ItemBinding.of(it) } + + BindingRecyclerViewAdapters.setAdapter(it, binding, items, null, null, null, null) + } + ) + fun cancellable(isCancellable: Boolean) = apply { setCancelable(isCancellable) } - fun applyView(binding: Binding, body: Binding.() -> Unit = {}) = - apply { - resetView() - this.binding.dialogBaseContainer.addView(binding.root) - binding.apply(body) - } + fun applyView( + binding: Binding, + body: Binding.() -> Unit = {} + ) = applyView(binding.root).also { binding.apply(body) } fun applyView(view: View) = apply { resetView() - this.binding.dialogBaseContainer.addView(view) + binding.dialogBaseContainer.addView(view) + view.updateLayoutParams { + width = ViewGroup.LayoutParams.MATCH_PARENT + height = ViewGroup.LayoutParams.WRAP_CONTENT + } } fun onDismiss(callback: OnDialogButtonClickListener) = @@ -203,4 +249,5 @@ class MagiskDialog @JvmOverloads constructor( //endregion } -typealias OnDialogButtonClickListener = (DialogInterface) -> Unit \ No newline at end of file +typealias OnDialogButtonClickListener = (DialogInterface) -> Unit +typealias OnDialogClickListener = (position: Int) -> Unit \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_magisk_base.xml b/app/src/main/res/layout/dialog_magisk_base.xml index a665c867f..7fa4bb7a3 100644 --- a/app/src/main/res/layout/dialog_magisk_base.xml +++ b/app/src/main/res/layout/dialog_magisk_base.xml @@ -93,6 +93,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" app:layout_constrainedHeight="true" + android:layout_marginTop="@dimen/l_50" app:layout_constraintBottom_toTopOf="@+id/dialog_base_space" app:layout_constraintLeft_toLeftOf="@+id/dialog_base_start" app:layout_constraintRight_toRightOf="@+id/dialog_base_end" @@ -108,7 +109,6 @@ gone="@{data.message.length == 0}" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginTop="@dimen/l_50" android:gravity="center" android:text="@{data.message}" android:textAppearance="@style/AppearanceFoundation.Body" diff --git a/app/src/main/res/layout/item_list_single_line.xml b/app/src/main/res/layout/item_list_single_line.xml new file mode 100644 index 000000000..e0f12d569 --- /dev/null +++ b/app/src/main/res/layout/item_list_single_line.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_settings_selector.xml b/app/src/main/res/layout/item_settings_selector.xml index d5f97e80c..401dc552d 100644 --- a/app/src/main/res/layout/item_settings_selector.xml +++ b/app/src/main/res/layout/item_settings_selector.xml @@ -19,7 +19,7 @@ style="@style/WidgetFoundation.Card" android:layout_width="match_parent" android:layout_height="wrap_content" - android:onClick="@{(view) -> callback.onItemPressed(view, item)}" + android:onClick="@{(view) -> item.onPressed(view, callback)}" tools:layout_gravity="center">