Skip to content

Commit

Permalink
AppCleaner: Highlight settings that require "USAGE_STATS" permission …
Browse files Browse the repository at this point in the history
…to function correctly.
  • Loading branch information
d4rken committed Sep 22, 2023
1 parent 698f9cd commit 50e5606
Show file tree
Hide file tree
Showing 10 changed files with 271 additions and 36 deletions.
1 change: 1 addition & 0 deletions app-common/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<string name="general_save_action">Save</string>
<string name="general_thanks_action">Thanks</string>
<string name="general_gotit_action">Got it</string>
<string name="general_fix_action">Fix</string>
<string name="general_edit_action">Edit</string>
<string name="general_na_label">N/A</string>
<string name="general_empty_label">Empty</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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

Expand All @@ -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<Preference>(settings.includeOtherUsersEnabled.keyName)?.apply {
summary =
summary.toString() + "\n" + getString(eu.darken.sdmse.common.R.string.general_root_required_message)
}

findPreference<Preference>(settings.minCacheSizeBytes.keyName)?.apply {
setOnPreferenceClickListener {
val dialogLayout = ViewPreferenceSeekbarBinding.inflate(layoutInflater, null, false)
Expand Down Expand Up @@ -140,5 +147,43 @@ class AppCleanerSettingsFragment : PreferenceFragment2() {
true
}
}

findPreference<Preference>(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
}
}
}
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -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<CaveatPreferenceView>().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")
}
}
Original file line number Diff line number Diff line change
@@ -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")
}
}
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -19,4 +21,11 @@ inline fun <reified T> ListPreference.setupWithEnum(preference: DataStoreValue<T
preference.valueBlocking = newValue
true
}
}
}

val PreferenceGroup.children: Sequence<Preference>
get() = sequence {
for (i in 0 until preferenceCount) {
yield(getPreference(i))
}
}
5 changes: 5 additions & 0 deletions app/src/main/res/layout/view_caveat_preference_group.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">

</androidx.constraintlayout.widget.ConstraintLayout>
40 changes: 40 additions & 0 deletions app/src/main/res/layout/view_caveat_preference_view.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginBottom="16dp"
android:background="@drawable/dashboard_progress_row_bg"
android:foreground="?selectableItemBackgroundBorderless"
android:paddingVertical="10dp"
android:paddingStart="16dp"
android:paddingEnd="0dp">

<com.google.android.material.textview.MaterialTextView
android:id="@+id/primary"
style="@style/TextAppearance.Material3.BodySmall"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/positive_action"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="XYZ" />

<com.google.android.material.textview.MaterialTextView
android:id="@+id/positive_action"
style="@style/TextAppearance.Material3.LabelMedium"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginHorizontal="16dp"
android:layout_marginVertical="8dp"
android:text="@string/general_fix_action"
android:textColor="?colorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:textAllCaps="true" />

</androidx.constraintlayout.widget.ConstraintLayout>
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,8 @@
<string name="setup_notification_body">Allow SD Maid to post notifications to inform you about on-going operations or important events.</string>
<string name="setup_notification_hint">Notifications are used sparingly 🤞</string>

<string name="setup_feature_requires_x_setup">This feature requires completion of the \"%s\" setup item.</string>

<string name="setup_acs_card_title">Accessibility service</string>
<string name="setup_acs_card_body">The accessibility service allows SD Maid to interact with your screen and automate tedious actions, e.g. tapping \"Clear cache\" on multiple screens.</string>
<string name="setup_acs_card_body2">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.</string>
Expand Down
Loading

0 comments on commit 50e5606

Please sign in to comment.