From 3efea47ca85fdca5fc63a650bd66c6c5fc664a3e Mon Sep 17 00:00:00 2001 From: Viktor De Pasquale Date: Mon, 2 Dec 2019 18:35:48 +0100 Subject: [PATCH] Updated settings to level functionality with the legacy --- .../com/topjohnwu/magisk/di/RedesignModule.kt | 2 +- .../redesign/settings/SettingsFragment.kt | 5 + .../magisk/redesign/settings/SettingsItems.kt | 122 +++++++++++++++--- .../redesign/settings/SettingsViewModel.kt | 93 ++++++++++++- .../res/layout/dialog_settings_app_name.xml | 52 ++++++++ .../main/res/layout/item_settings_blank.xml | 2 +- .../main/res/layout/item_settings_input.xml | 4 +- 7 files changed, 254 insertions(+), 26 deletions(-) create mode 100644 app/src/main/res/layout/dialog_settings_app_name.xml diff --git a/app/src/main/java/com/topjohnwu/magisk/di/RedesignModule.kt b/app/src/main/java/com/topjohnwu/magisk/di/RedesignModule.kt index ff274352d..c5d93ac71 100644 --- a/app/src/main/java/com/topjohnwu/magisk/di/RedesignModule.kt +++ b/app/src/main/java/com/topjohnwu/magisk/di/RedesignModule.kt @@ -23,7 +23,7 @@ val redesignModule = module { viewModel { ModuleViewModel(get(), get(), get()) } viewModel { RequestViewModel() } viewModel { SafetynetViewModel(get()) } - viewModel { SettingsViewModel() } + viewModel { SettingsViewModel(get()) } viewModel { SuperuserViewModel(get(), get(), get()) } viewModel { ThemeViewModel() } viewModel { InstallViewModel() } diff --git a/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsFragment.kt b/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsFragment.kt index 6f665624b..8ac1efad5 100644 --- a/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsFragment.kt +++ b/app/src/main/java/com/topjohnwu/magisk/redesign/settings/SettingsFragment.kt @@ -19,4 +19,9 @@ class SettingsFragment : CompatFragment 14 || value.isBlank() + + override val intermediate: String? + get() = if (isError) null else value + + override fun getView(context: Context) = DialogSettingsAppNameBinding + .inflate(LayoutInflater.from(context)).also { it.data = this }.root + + override fun refresh() { + isEnabled = Info.env.isActive + } } object Restore : SettingsItem.Blank() { @@ -129,11 +151,7 @@ object UpdateChannelUrl : SettingsItem.Input() { notifyChange(BR.result) } - init { - updateState() - } - - fun updateState() { + override fun refresh() { isEnabled = UpdateChannel.value == Config.Value.CUSTOM_CHANNEL } @@ -144,19 +162,43 @@ 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) { Config.checkUpdate = it } + override var value by dataObservable(Config.checkUpdate) { + Config.checkUpdate = it + Utils.scheduleUpdateCheck(get()) + } } // 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() + + override fun refresh() { + isEnabled = Info.env.isActive + } } 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 fun refresh() { + isEnabled = BiometricHelper.isSupported && Utils.showSuperUser() + if (!isEnabled) { + value = false + } + } +} + +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 fun refresh() { + isEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.O && Utils.showSuperUser() + } } // --- Magisk @@ -168,13 +210,35 @@ object Magisk : SettingsItem.Section() { 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 } + override var value by dataObservable(Config.coreOnly) { + if (Config.coreOnly == it) return@dataObservable + Config.coreOnly = it + when { + it -> runCatching { Const.MAGISK_DISABLE_FILE.createNewFile() } + else -> Const.MAGISK_DISABLE_FILE.delete() + } + Utils.toast(R.string.settings_reboot_toast, Toast.LENGTH_LONG) + } + + override fun refresh() { + isEnabled = Info.env.isActive + } } 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 } + override var value by dataObservable(Config.magiskHide) { + Config.magiskHide = it + when { + it -> Shell.su("magiskhide --enable").submit() + else -> Shell.su("magiskhide --disable").submit() + } + } + + override fun refresh() { + isEnabled = Info.env.isActive + } } // --- Superuser @@ -197,6 +261,10 @@ object AccessMode : SettingsItem.Selector() { resources.getStringArray(R.array.value_array) ) } + + override fun refresh() { + isEnabled = Utils.showSuperUser() + } } object MultiuserMode : SettingsItem.Selector() { @@ -213,6 +281,10 @@ object MultiuserMode : SettingsItem.Selector() { resources.getStringArray(R.array.value_array) ) } + + override fun refresh() { + isEnabled = Const.USER_ID <= 0 && Utils.showSuperUser() + } } object MountNamespaceMode : SettingsItem.Selector() { @@ -229,6 +301,10 @@ object MountNamespaceMode : SettingsItem.Selector() { resources.getStringArray(R.array.value_array) ) } + + override fun refresh() { + isEnabled = Utils.showSuperUser() + } } object AutomaticResponse : SettingsItem.Selector() { @@ -245,6 +321,10 @@ object AutomaticResponse : SettingsItem.Selector() { resources.getStringArray(R.array.value_array) ) } + + override fun refresh() { + isEnabled = Utils.showSuperUser() + } } object RequestTimeout : SettingsItem.Selector() { @@ -263,6 +343,10 @@ object RequestTimeout : SettingsItem.Selector() { val currentValue = Config.suDefaultTimeout.toString() value = entryValues.indexOfFirst { it == currentValue } } + + override fun refresh() { + isEnabled = Utils.showSuperUser() + } } object SUNotification : SettingsItem.Selector() { @@ -279,4 +363,8 @@ object SUNotification : SettingsItem.Selector() { resources.getStringArray(R.array.value_array) ) } + + override fun refresh() { + isEnabled = Utils.showSuperUser() + } } \ 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 c431f10c0..d3773b0f3 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,30 +1,47 @@ 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 +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.events.DieEvent +import com.topjohnwu.magisk.model.events.PermissionEvent +import com.topjohnwu.magisk.model.events.RecreateEvent +import com.topjohnwu.magisk.model.events.dialog.BiometricDialog import com.topjohnwu.magisk.model.navigation.Navigation import com.topjohnwu.magisk.redesign.compat.CompatViewModel 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 : CompatViewModel(), SettingsItem.Callback { +class SettingsViewModel( + private val repositoryDao: RepoDao +) : CompatViewModel(), SettingsItem.Callback { val adapter = adapterOf() val itemBinding = itemBindingOf { it.bindExtra(BR.callback, this) } @@ -34,10 +51,10 @@ class SettingsViewModel : CompatViewModel(), SettingsItem.Callback { Manager, UpdateChannel, UpdateChannelUrl, ClearRepoCache, HideOrRestore(), UpdateChecker, - SystemlessHosts, Biometrics, + Biometrics, Reauthenticate, Magisk, - SafeMode, MagiskHide, + SafeMode, MagiskHide, SystemlessHosts, Superuser, AccessMode, MultiuserMode, MountNamespaceMode, AutomaticResponse, RequestTimeout, @@ -45,20 +62,74 @@ class SettingsViewModel : CompatViewModel(), SettingsItem.Callback { ) override fun onItemPressed(view: View, item: SettingsItem) = when (item) { + is DownloadPath -> requireRWPermission() + else -> Unit + } + + override fun onItemChanged(view: View, item: SettingsItem) = when (item) { // use only instances you want, don't declare everything is Theme -> Navigation.theme().publish() is Redesign -> DieEvent().publish() + is Language -> RecreateEvent().publish() + is UpdateChannel -> openUrlIfNecessary(view) + is Biometrics -> authenticateOrRevert() + is ClearRepoCache -> clearRepoCache() + is SystemlessHosts -> createHosts() + is Hide -> updateManager(hide = true) + is Restore -> updateManager(hide = false) + else -> Unit } private fun openUrlIfNecessary(view: View) { - UpdateChannelUrl.updateState() + UpdateChannelUrl.refresh() if (UpdateChannelUrl.value.isBlank()) { UpdateChannelUrl.onPressed(view, this@SettingsViewModel) } } + private fun authenticateOrRevert() { + // immediately revert the preference + Biometrics.value = !Biometrics.value + BiometricDialog { + // allow the change on success + onSuccess { Biometrics.value = !Biometrics.value } + }.publish() + } + + private fun clearRepoCache() { + Completable.fromAction { repositoryDao.clear() } + .subscribeK { Utils.toast(R.string.repo_cache_cleared, Toast.LENGTH_SHORT) } + } + + private fun createHosts() { + Shell.su("add_hosts_module").submit { + Utils.toast(R.string.settings_hosts_toast, Toast.LENGTH_SHORT) + } + } + + private fun requireRWPermission() { + val callback = PublishSubject.create() + callback.subscribeK { if (!it) requireRWPermission() } + PermissionEvent( + listOf( + Manifest.permission.READ_EXTERNAL_STORAGE, + Manifest.permission.WRITE_EXTERNAL_STORAGE + ), callback + ).publish() + } + + private fun updateManager(hide: Boolean) { + if (hide) { + PatchAPK.hideManager(get(), Hide.value) + } else { + DownloadService(get()) { + subject = DownloadSubject.Manager(Configuration.APK.Restore) + } + } + } + } sealed class SettingsItem : ObservableItem() { @@ -81,7 +152,7 @@ sealed class SettingsItem : ObservableItem() { @CallSuper open fun onPressed(view: View, callback: Callback) { - callback.onItemPressed(view, this) + callback.onItemChanged(view, this) // notify only after the callback invocation; callback can invalidate the backing data, // which wouldn't be recognized with reverse approach @@ -90,6 +161,8 @@ sealed class SettingsItem : ObservableItem() { notifyChange(BR.description) } + open fun refresh() {} + override fun onBindingBound(binding: ViewDataBinding) { super.onBindingBound(binding) if (isFullSpan) { @@ -105,6 +178,7 @@ sealed class SettingsItem : ObservableItem() { interface Callback { fun onItemPressed(view: View, item: SettingsItem) + fun onItemChanged(view: View, item: SettingsItem) } // --- @@ -131,6 +205,7 @@ sealed class SettingsItem : ObservableItem() { 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) } @@ -147,11 +222,13 @@ sealed class SettingsItem : ObservableItem() { 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)) @@ -210,6 +287,7 @@ sealed class SettingsItem : ObservableItem() { 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) { @@ -229,6 +307,11 @@ sealed class SettingsItem : ObservableItem() { 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() { diff --git a/app/src/main/res/layout/dialog_settings_app_name.xml b/app/src/main/res/layout/dialog_settings_app_name.xml new file mode 100644 index 000000000..f0e325396 --- /dev/null +++ b/app/src/main/res/layout/dialog_settings_app_name.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_settings_blank.xml b/app/src/main/res/layout/item_settings_blank.xml index f8b5be8e3..a6df8aad7 100644 --- a/app/src/main/res/layout/item_settings_blank.xml +++ b/app/src/main/res/layout/item_settings_blank.xml @@ -21,7 +21,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:alpha="@{item.enabled ? 1f : .5f}" - android:onClick="@{(view) -> callback.onItemPressed(view, item)}" + android:onClick="@{(view) -> callback.onItemChanged(view, item)}" tools:layout_gravity="center">