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 new file mode 100644 index 000000000..4ebb43f6a --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsItems.kt @@ -0,0 +1,103 @@ +package com.topjohnwu.magisk.redesign.settings + +import android.content.Context +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.utils.asTransitive + +// --- Customization + +object Customization : SettingsItem.Section() { + override val title = "Customization".asTransitive() +} + +object Redesign : SettingsItem.Toggle() { + override val title = "Redesign".asTransitive() + override val description = + "Select this to disable redesign. App will automatically shut down".asTransitive() + override var value: Boolean by dataObservable(Config.redesign) { Config.redesign = it } +} + +object Theme : SettingsItem.Blank() { + override val icon = R.drawable.ic_paint + override val title = R.string.section_theme.asTransitive() +} + +// --- Manager + +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() +} + +object Hide : SettingsItem.Blank() { + override val title = R.string.settings_hide_manager_title.asTransitive() + override val description = R.string.settings_hide_manager_summary.asTransitive() +} + +object Restore : SettingsItem.Blank() { + override val title = R.string.settings_restore_manager_title.asTransitive() + override val description = R.string.settings_restore_manager_summary.asTransitive() +} + +@Suppress("FunctionName") +fun HideOrRestore() = + if (get().packageName == BuildConfig.APPLICATION_ID) Hide else Restore + +object DownloadPath +object UpdateChannel +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) { Config.checkUpdate = it } +} + +// check whether is module already installed beforehand? +object SystemlessHosts : SettingsItem.Blank() { + override val title = R.string.settings_hosts_title.asTransitive() + override val description = R.string.settings_hosts_summary.asTransitive() +} + +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 } +} + +// --- Magisk + +object Magisk : SettingsItem.Section() { + override val title = R.string.magisk.asTransitive() +} + +object SafeMode : SettingsItem.Toggle() { + override val title = R.string.settings_core_only_title.asTransitive() + override val description = R.string.settings_core_only_summary.asTransitive() + override var value by dataObservable(Config.coreOnly) { Config.coreOnly = it } +} + +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) { Config.magiskHide = it } +} + +// --- Superuser + +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 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 2dbcce10a..6a261ec2a 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,26 +1,149 @@ package com.topjohnwu.magisk.redesign.settings -import com.topjohnwu.magisk.Config -import com.topjohnwu.magisk.extensions.addOnPropertyChangedCallback -import com.topjohnwu.magisk.extensions.toggle +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.model.entity.recycler.ObservableItem import com.topjohnwu.magisk.model.events.DieEvent import com.topjohnwu.magisk.model.navigation.Navigation import com.topjohnwu.magisk.redesign.compat.CompatViewModel -import com.topjohnwu.magisk.utils.KObservableField +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.TransitiveText +import com.topjohnwu.magisk.view.MagiskDialog +import kotlin.properties.ObservableProperty +import kotlin.reflect.KProperty -class SettingsViewModel : CompatViewModel() { +class SettingsViewModel : CompatViewModel(), SettingsItem.Callback { - val redesign = KObservableField(Config.redesign) + 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 + ) - init { - //todo make observable preference - redesign.addOnPropertyChangedCallback { - Config.redesign = redesign.value - DieEvent().publish() + override fun onItemPressed(view: View, item: SettingsItem) = when (item) { + // use only instances you want, don't declare everything + Theme -> Navigation.theme().publish() + Redesign -> DieEvent().publish() + else -> Unit + } + +} + +sealed class SettingsItem : ObservableItem() { + + @Bindable + open val icon: Int = 0 + @Bindable + open val title: TransitiveText = TransitiveText.empty + @Bindable + open val description: TransitiveText = TransitiveText.empty + + protected open val isFullSpan: Boolean = false + + @CallSuper + open fun onPressed(view: View, callback: Callback) { + callback.onItemPressed(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) + } + + override fun onBindingBound(binding: ViewDataBinding) { + super.onBindingBound(binding) + if (isFullSpan) { + val params = binding.root.layoutParams as? StaggeredGridLayoutManager.LayoutParams + params?.isFullSpan = true } } - fun toggle(item: KObservableField) = item.toggle() - fun themePressed() = Navigation.theme().publish() + override fun itemSameAs(other: SettingsItem) = this === other + override fun contentSameAs(other: SettingsItem) = itemSameAs(other) + + // --- + + interface Callback { + fun onItemPressed(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) { + 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 Selector : Value() { + + override val layoutRes = R.layout.item_settings_selector + + override fun onPressed(view: View, callback: Callback) { + MagiskDialog(view.context) + //.applyAdapter() + super.onPressed(view, callback) + } + + } + + abstract class Blank : SettingsItem() { + + override val layoutRes = R.layout.item_settings_blank + + } + + 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/utils/DataBindingAdapters.kt b/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt index 3d76715e9..69462e5bd 100644 --- a/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt +++ b/app/src/main/java/com/topjohnwu/magisk/utils/DataBindingAdapters.kt @@ -296,18 +296,24 @@ fun TextSwitcher.setTextBinding(text: CharSequence) { "android:layout_marginTop", "android:layout_marginRight", "android:layout_marginBottom", + "android:layout_marginStart", + "android:layout_marginEnd", requireAll = false ) fun View.setMargins( marginLeft: Int?, marginTop: Int?, marginRight: Int?, - marginBottom: Int? + marginBottom: Int?, + marginStart: Int?, + marginEnd: Int? ) = updateLayoutParams { marginLeft?.let { leftMargin = it } marginTop?.let { topMargin = it } marginRight?.let { rightMargin = it } marginBottom?.let { bottomMargin = it } + marginStart?.let { this.marginStart = it } + marginEnd?.let { this.marginEnd = it } } @BindingAdapter("nestedScrollingEnabled") @@ -455,9 +461,4 @@ fun View.setRotationNotAnimated(rotation: Int) { @BindingAdapter("android:text") fun TextView.setTextSafe(text: Int) { if (text == 0) this.text = null else setText(text) -} - -@BindingAdapter("android:theme") -fun View.setThemeCompat(theme: Int) { - } \ No newline at end of file diff --git a/app/src/main/java/com/topjohnwu/magisk/utils/TransitiveText.kt b/app/src/main/java/com/topjohnwu/magisk/utils/TransitiveText.kt new file mode 100644 index 000000000..f901a5889 --- /dev/null +++ b/app/src/main/java/com/topjohnwu/magisk/utils/TransitiveText.kt @@ -0,0 +1,54 @@ +package com.topjohnwu.magisk.utils + +import android.content.res.Resources +import android.widget.TextView +import androidx.databinding.BindingAdapter +import androidx.databinding.InverseBindingAdapter + +sealed class TransitiveText { + + abstract val isEmpty: Boolean + abstract fun getText(resources: Resources): CharSequence + + // --- + + class String( + private val value: CharSequence + ) : TransitiveText() { + + override val isEmpty = value.isEmpty() + override fun getText(resources: Resources) = value + + } + + class Res( + private val value: Int, + private vararg val params: Any + ) : TransitiveText() { + + override val isEmpty = value == 0 + override fun getText(resources: Resources) = + resources.getString(value, *params) + + } + + // --- + + companion object { + val empty = String("") + } + +} + + +fun Int.asTransitive(vararg params: Any) = TransitiveText.Res(this, *params) +fun CharSequence.asTransitive() = TransitiveText.String(this) + + +@BindingAdapter("android:text") +fun TextView.setText(text: TransitiveText) { + this.text = text.getText(resources) +} + +@InverseBindingAdapter(attribute = "android:text", event = "android:textAttrChanged") +fun TextView.getTransitiveText() = text.asTransitive() \ No newline at end of file diff --git a/app/src/main/res/color/color_primary_transient.xml b/app/src/main/res/color/color_primary_transient.xml index b792af898..52adbdb4e 100644 --- a/app/src/main/res/color/color_primary_transient.xml +++ b/app/src/main/res/color/color_primary_transient.xml @@ -1,5 +1,6 @@ + \ No newline at end of file diff --git a/app/src/main/res/drawable/divider_l_50.xml b/app/src/main/res/drawable/divider_l_50.xml new file mode 100644 index 000000000..51b4e1496 --- /dev/null +++ b/app/src/main/res/drawable/divider_l_50.xml @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings_md2.xml b/app/src/main/res/layout/fragment_settings_md2.xml index 4eb129974..cefa8c7a5 100644 --- a/app/src/main/res/layout/fragment_settings_md2.xml +++ b/app/src/main/res/layout/fragment_settings_md2.xml @@ -5,130 +5,33 @@ + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + tools:listitem="@layout/item_settings_toggle" + tools:paddingTop="@dimen/l1" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_theme_md2.xml b/app/src/main/res/layout/fragment_theme_md2.xml index 23d0f3ef5..d15b98330 100644 --- a/app/src/main/res/layout/fragment_theme_md2.xml +++ b/app/src/main/res/layout/fragment_theme_md2.xml @@ -31,7 +31,7 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_settings_section.xml b/app/src/main/res/layout/item_settings_section.xml new file mode 100644 index 000000000..7c6a29e10 --- /dev/null +++ b/app/src/main/res/layout/item_settings_section.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + \ 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 new file mode 100644 index 000000000..d5f97e80c --- /dev/null +++ b/app/src/main/res/layout/item_settings_selector.xml @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_settings_toggle.xml b/app/src/main/res/layout/item_settings_toggle.xml new file mode 100644 index 000000000..7c6a5acd3 --- /dev/null +++ b/app/src/main/res/layout/item_settings_toggle.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/item_tappable_headline.xml b/app/src/main/res/layout/item_tappable_headline.xml index 61b0c5f13..9e9f324e3 100644 --- a/app/src/main/res/layout/item_tappable_headline.xml +++ b/app/src/main/res/layout/item_tappable_headline.xml @@ -16,7 +16,7 @@ ?attr/colorOnSurface + + @@ -74,6 +78,14 @@ ?attr/colorOnSurfaceVariant + + + + diff --git a/app/src/main/res/values/styles_md2_impl.xml b/app/src/main/res/values/styles_md2_impl.xml index 86f10ac27..841cb6325 100644 --- a/app/src/main/res/values/styles_md2_impl.xml +++ b/app/src/main/res/values/styles_md2_impl.xml @@ -42,10 +42,6 @@ ?colorPrimary - - + + + \ No newline at end of file