From 12e8c4a4bd259a200dbb0c77e606a564badcb79d Mon Sep 17 00:00:00 2001 From: darken Date: Fri, 22 Sep 2023 20:04:47 +0200 Subject: [PATCH] AppCleaner: Highlight settings that require "USAGE_STATS" permission to function correctly. --- app-common/src/main/res/values/strings.xml | 1 + .../ui/settings/AppCleanerSettingsFragment.kt | 53 ++++++++++++++-- .../settings/AppCleanerSettingsViewModel.kt | 23 ++++--- .../preferences/CaveatPreferenceGroup.kt | 61 +++++++++++++++++++ .../preferences/CaveatPreferenceView.kt | 58 ++++++++++++++++++ .../preferences/PreferenceExtensions.kt | 11 +++- .../layout/view_caveat_preference_group.xml | 5 ++ .../layout/view_caveat_preference_view.xml | 40 ++++++++++++ app/src/main/res/values/strings.xml | 2 + .../main/res/xml/preferences_appcleaner.xml | 53 +++++++++------- 10 files changed, 271 insertions(+), 36 deletions(-) create mode 100644 app/src/main/java/eu/darken/sdmse/common/preferences/CaveatPreferenceGroup.kt create mode 100644 app/src/main/java/eu/darken/sdmse/common/preferences/CaveatPreferenceView.kt create mode 100644 app/src/main/res/layout/view_caveat_preference_group.xml create mode 100644 app/src/main/res/layout/view_caveat_preference_view.xml diff --git a/app-common/src/main/res/values/strings.xml b/app-common/src/main/res/values/strings.xml index 21e6c18e0..286b77066 100644 --- a/app-common/src/main/res/values/strings.xml +++ b/app-common/src/main/res/values/strings.xml @@ -25,6 +25,7 @@ Save Thanks Got it + Fix Edit N/A Empty diff --git a/app/src/main/java/eu/darken/sdmse/appcleaner/ui/settings/AppCleanerSettingsFragment.kt b/app/src/main/java/eu/darken/sdmse/appcleaner/ui/settings/AppCleanerSettingsFragment.kt index 2aea46850..92b52e78d 100644 --- a/app/src/main/java/eu/darken/sdmse/appcleaner/ui/settings/AppCleanerSettingsFragment.kt +++ b/app/src/main/java/eu/darken/sdmse/appcleaner/ui/settings/AppCleanerSettingsFragment.kt @@ -1,7 +1,9 @@ package eu.darken.sdmse.appcleaner.ui.settings +import android.os.Bundle import android.text.format.DateUtils import android.text.format.Formatter +import android.view.View import androidx.annotation.Keep import androidx.fragment.app.viewModels import androidx.preference.Preference @@ -11,9 +13,12 @@ import dagger.hilt.android.AndroidEntryPoint import eu.darken.sdmse.R import eu.darken.sdmse.appcleaner.core.AppCleanerSettings import eu.darken.sdmse.common.datastore.valueBlocking +import eu.darken.sdmse.common.observe2 +import eu.darken.sdmse.common.preferences.CaveatPreferenceGroup import eu.darken.sdmse.common.uix.PreferenceFragment2 import eu.darken.sdmse.databinding.AppcontrolSettingsAgeSettingDialogBinding import eu.darken.sdmse.databinding.ViewPreferenceSeekbarBinding +import eu.darken.sdmse.main.ui.settings.SettingsFragmentDirections import java.time.Duration import javax.inject.Inject @@ -26,12 +31,14 @@ class AppCleanerSettingsFragment : PreferenceFragment2() { @Inject override lateinit var settings: AppCleanerSettings override val preferenceFile: Int = R.xml.preferences_appcleaner + private val includeRunningCaveat: CaveatPreferenceGroup + get() = findPreference("include.runningapps.enabled.caveat")!! + private val includeInaccessibleCaveat: CaveatPreferenceGroup + get() = findPreference("include.inaccessible.enabled.caveat")!! + override fun onPreferencesCreated() { super.onPreferencesCreated() - findPreference(settings.includeOtherUsersEnabled.keyName)?.apply { - summary = - summary.toString() + "\n" + getString(eu.darken.sdmse.common.R.string.general_root_required_message) - } + findPreference(settings.minCacheSizeBytes.keyName)?.apply { setOnPreferenceClickListener { val dialogLayout = ViewPreferenceSeekbarBinding.inflate(layoutInflater, null, false) @@ -140,5 +147,43 @@ class AppCleanerSettingsFragment : PreferenceFragment2() { true } } + + findPreference(settings.includeOtherUsersEnabled.keyName)?.apply { + summary = + summary.toString() + "\n" + getString(eu.darken.sdmse.common.R.string.general_root_required_message) + } + + includeRunningCaveat.apply { + caveatMessage = getString( + R.string.setup_feature_requires_x_setup, + getString(R.string.setup_usagestats_title) + ) + caveatAction = getString(eu.darken.sdmse.common.R.string.general_fix_action) + caveatClickListener = { + SettingsFragmentDirections.goToSetup(showCompleted = true).navigate() + true + } + } + + includeInaccessibleCaveat.apply { + caveatMessage = getString( + R.string.setup_feature_requires_x_setup, + getString(R.string.setup_usagestats_title) + ) + caveatAction = getString(eu.darken.sdmse.common.R.string.general_fix_action) + caveatClickListener = { + SettingsFragmentDirections.goToSetup(showCompleted = true).navigate() + true + } + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + vm.state.observe2(this) { state -> + includeRunningCaveat.showCaveat = !state.hasUsageStats + includeInaccessibleCaveat.showCaveat = !state.hasUsageStats + } } } \ No newline at end of file diff --git a/app/src/main/java/eu/darken/sdmse/appcleaner/ui/settings/AppCleanerSettingsViewModel.kt b/app/src/main/java/eu/darken/sdmse/appcleaner/ui/settings/AppCleanerSettingsViewModel.kt index c8165e185..c1714919b 100644 --- a/app/src/main/java/eu/darken/sdmse/appcleaner/ui/settings/AppCleanerSettingsViewModel.kt +++ b/app/src/main/java/eu/darken/sdmse/appcleaner/ui/settings/AppCleanerSettingsViewModel.kt @@ -1,33 +1,38 @@ package eu.darken.sdmse.appcleaner.ui.settings -import androidx.lifecycle.SavedStateHandle import dagger.hilt.android.lifecycle.HiltViewModel import eu.darken.sdmse.common.coroutine.DispatcherProvider import eu.darken.sdmse.common.debug.logging.logTag import eu.darken.sdmse.common.root.RootManager import eu.darken.sdmse.common.root.canUseRootNow import eu.darken.sdmse.common.uix.ViewModel3 +import eu.darken.sdmse.setup.usagestats.UsageStatsSetupModule +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.map import javax.inject.Inject @HiltViewModel class AppCleanerSettingsViewModel @Inject constructor( - private val handle: SavedStateHandle, - private val dispatcherProvider: DispatcherProvider, + dispatcherProvider: DispatcherProvider, private val rootManager: RootManager, + usageStatsSetupModule: UsageStatsSetupModule, ) : ViewModel3(dispatcherProvider) { - val state = flow { - emit( - State( - isRooted = rootManager.canUseRootNow() - ) + val state = combine( + flow { emit(rootManager.canUseRootNow()) }, + usageStatsSetupModule.state.map { it.isComplete } + ) { canUseRoot, hasUsageStats -> + State( + isRooted = canUseRoot, + hasUsageStats = hasUsageStats, ) }.asLiveData2() data class State( - val isRooted: Boolean + val isRooted: Boolean, + val hasUsageStats: Boolean, ) companion object { diff --git a/app/src/main/java/eu/darken/sdmse/common/preferences/CaveatPreferenceGroup.kt b/app/src/main/java/eu/darken/sdmse/common/preferences/CaveatPreferenceGroup.kt new file mode 100644 index 000000000..c4ebbc3aa --- /dev/null +++ b/app/src/main/java/eu/darken/sdmse/common/preferences/CaveatPreferenceGroup.kt @@ -0,0 +1,61 @@ +package eu.darken.sdmse.common.preferences + +import android.content.Context +import android.util.AttributeSet +import androidx.annotation.AttrRes +import androidx.annotation.StyleRes +import androidx.preference.PreferenceGroup +import androidx.preference.children +import eu.darken.sdmse.R +import eu.darken.sdmse.common.debug.logging.logTag + + +class CaveatPreferenceGroup @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + @AttrRes defStyleAttr: Int = androidx.preference.R.attr.preferenceStyle, + @StyleRes defStyleRes: Int = 0, +) : PreferenceGroup(context, attrs, defStyleAttr, defStyleRes) { + + init { + layoutResource = R.layout.view_caveat_preference_group + isPersistent = false + } + + private val caveatEntry: CaveatPreferenceView + get() = children.filterIsInstance().first() + + var caveatMessage: String? = null + set(value) { + field = value + caveatEntry.caveatMessage = value + } + + var caveatAction: String? = null + set(value) { + field = value + caveatEntry.caveatAction = value + } + + var showCaveat: Boolean = false + set(value) { + field = value + caveatEntry.isVisible = value + } + + var caveatClickListener: (() -> Boolean)? = null + set(value) { + field = value + caveatEntry.apply { + if (value != null) { + this.setOnPreferenceClickListener { value.invoke() } + } else { + this.onPreferenceClickListener = null + } + } + } + + companion object { + private val TAG = logTag("CaveatPreferenceGroup") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/sdmse/common/preferences/CaveatPreferenceView.kt b/app/src/main/java/eu/darken/sdmse/common/preferences/CaveatPreferenceView.kt new file mode 100644 index 000000000..3b39ba49e --- /dev/null +++ b/app/src/main/java/eu/darken/sdmse/common/preferences/CaveatPreferenceView.kt @@ -0,0 +1,58 @@ +package eu.darken.sdmse.common.preferences + +import android.content.Context +import android.util.AttributeSet +import android.view.ViewGroup +import androidx.annotation.AttrRes +import androidx.annotation.StyleRes +import androidx.core.view.children +import androidx.preference.PreferenceGroup +import androidx.preference.PreferenceViewHolder +import eu.darken.sdmse.R +import eu.darken.sdmse.common.MimeTypes.Json.value +import eu.darken.sdmse.common.debug.logging.logTag +import eu.darken.sdmse.databinding.ViewCaveatPreferenceViewBinding + + +class CaveatPreferenceView @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + @AttrRes defStyleAttr: Int = androidx.preference.R.attr.preferenceStyle, + @StyleRes defStyleRes: Int = 0, +) : PreferenceGroup(context, attrs, defStyleAttr, defStyleRes) { + + init { + layoutResource = R.layout.view_caveat_preference_view + isVisible = false + isPersistent = false + } + + var caveatMessage: String? = summary?.toString() + set(value) { + field = value + notifyChanged() + } + + var caveatAction: String? = title?.toString() + set(value) { + field = value + notifyChanged() + } + + override fun onBindViewHolder(holder: PreferenceViewHolder) { + super.onBindViewHolder(holder) + ViewCaveatPreferenceViewBinding.bind(holder.itemView).apply { + primary.text = caveatMessage + positiveAction.text = caveatAction + positiveAction.setOnClickListener { + @Suppress("RestrictedApi") + this@CaveatPreferenceView.performClick() + } + (holder.itemView as ViewGroup).children + } + } + + companion object { + private val TAG = logTag("CaveatPreferenceView") + } +} \ No newline at end of file diff --git a/app/src/main/java/eu/darken/sdmse/common/preferences/PreferenceExtensions.kt b/app/src/main/java/eu/darken/sdmse/common/preferences/PreferenceExtensions.kt index 701f91cfd..cfbc04b54 100644 --- a/app/src/main/java/eu/darken/sdmse/common/preferences/PreferenceExtensions.kt +++ b/app/src/main/java/eu/darken/sdmse/common/preferences/PreferenceExtensions.kt @@ -1,6 +1,8 @@ package eu.darken.sdmse.common.preferences import androidx.preference.ListPreference +import androidx.preference.Preference +import androidx.preference.PreferenceGroup import eu.darken.sdmse.common.datastore.DataStoreValue import eu.darken.sdmse.common.datastore.valueBlocking @@ -19,4 +21,11 @@ inline fun ListPreference.setupWithEnum(preference: DataStoreValue + get() = sequence { + for (i in 0 until preferenceCount) { + yield(getPreference(i)) + } + } diff --git a/app/src/main/res/layout/view_caveat_preference_group.xml b/app/src/main/res/layout/view_caveat_preference_group.xml new file mode 100644 index 000000000..726b86a78 --- /dev/null +++ b/app/src/main/res/layout/view_caveat_preference_group.xml @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_caveat_preference_view.xml b/app/src/main/res/layout/view_caveat_preference_view.xml new file mode 100644 index 000000000..e314028d2 --- /dev/null +++ b/app/src/main/res/layout/view_caveat_preference_view.xml @@ -0,0 +1,40 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0b5aaca2b..486546ace 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -141,6 +141,8 @@ Allow SD Maid to post notifications to inform you about on-going operations or important events. Notifications are used sparingly 🤞 + This feature requires completion of the \"%s\" setup item. + Accessibility service The accessibility service allows SD Maid to interact with your screen and automate tedious actions, e.g. tapping \"Clear cache\" on multiple screens. SD Maid will not use the accessibility service without your consent, will always display a prominent (cancelable) indicator while active and will not use it to collect information. diff --git a/app/src/main/res/xml/preferences_appcleaner.xml b/app/src/main/res/xml/preferences_appcleaner.xml index a6c5bc3ee..72d6d2598 100644 --- a/app/src/main/res/xml/preferences_appcleaner.xml +++ b/app/src/main/res/xml/preferences_appcleaner.xml @@ -10,30 +10,39 @@ app:title="@string/appcleaner_include_systemapps_label" /> + - + - + + + + + + + + + @@ -101,20 +110,20 @@ app:title="@string/appcleaner_filter_gamefiles_label" /> @@ -122,14 +131,14 @@