Skip to content

Commit

Permalink
AppCleaner, Deduplicator: Allow manual input for "minimum size"
Browse files Browse the repository at this point in the history
Closes #1438
Closes #1484
  • Loading branch information
d4rken committed Dec 4, 2024
1 parent 820aef3 commit e9fb92a
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ 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
Expand All @@ -17,7 +16,6 @@ import eu.darken.sdmse.common.observe2
import eu.darken.sdmse.common.preferences.BadgedCheckboxPreference
import eu.darken.sdmse.common.uix.PreferenceFragment2
import eu.darken.sdmse.databinding.AppcontrolSettingsAgeSettingDialogBinding
import eu.darken.sdmse.databinding.ViewPreferenceSeekbarBinding
import eu.darken.sdmse.setup.SetupModule
import eu.darken.sdmse.setup.showFixSetupHint
import java.time.Duration
Expand Down Expand Up @@ -46,40 +44,13 @@ class AppCleanerSettingsFragment : PreferenceFragment2() {

findPreference<Preference>(settings.minCacheSizeBytes.keyName)?.apply {
setOnPreferenceClickListener {
val dialogLayout = ViewPreferenceSeekbarBinding.inflate(layoutInflater, null, false)
dialogLayout.apply {
slider.valueFrom = 0f
slider.valueTo = 100 * 1024f
slider.value = (settings.minCacheSizeBytes.valueBlocking / 1024f).coerceAtMost(slider.valueTo)

val getSliderText = { value: Float ->
val size = value.toLong() * 1024L
Formatter.formatShortFileSize(requireContext(), size)
}
slider.setLabelFormatter { getSliderText(it) }
sliderValue.text = getSliderText(slider.value)

slider.addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
override fun onStartTrackingTouch(slider: Slider) {
sliderValue.text = getSliderText(slider.value)
}

override fun onStopTrackingTouch(slider: Slider) {
sliderValue.text = getSliderText(slider.value)
}
})
}
MaterialAlertDialogBuilder(requireContext()).apply {
setTitle(R.string.appcleaner_include_minimumsize_label)
setView(dialogLayout.root)
setPositiveButton(eu.darken.sdmse.common.R.string.general_save_action) { _, _ ->
settings.minCacheSizeBytes.valueBlocking = dialogLayout.slider.value.toLong() * 1024L
}
setNegativeButton(eu.darken.sdmse.common.R.string.general_cancel_action) { _, _ -> }
setNeutralButton(eu.darken.sdmse.common.R.string.general_reset_action) { _, _ ->
settings.minCacheSizeBytes.valueBlocking = AppCleanerSettings.MIN_CACHE_SIZE_DEFAULT
}
}.show()
SizeInputDialog(
requireActivity(),
titleRes = R.string.appcleaner_include_minimumsize_label,
currentSize = settings.minCacheSizeBytes.valueBlocking,
onReset = { settings.minCacheSizeBytes.valueBlocking = AppCleanerSettings.MIN_CACHE_SIZE_DEFAULT },
onSave = { settings.minCacheSizeBytes.valueBlocking = it }
).show()
true
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package eu.darken.sdmse.appcleaner.ui.settings

import android.app.Activity
import android.content.Context
import android.text.format.Formatter
import androidx.annotation.StringRes
import androidx.core.widget.addTextChangedListener
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import eu.darken.sdmse.databinding.ViewPreferenceInputSizeBinding

class SizeInputDialog(
private val activity: Activity,
@StringRes private val titleRes: Int,
private val minimumSize: Long = 0,
private val maximumSize: Long = 100 * 1000 * 1024L,
private val currentSize: Long,
private val onReset: () -> Unit,
private val onCancel: () -> Unit = {},
private val onSave: (Long) -> Unit,
) {

private val context: Context
get() = activity

private fun parseSize(input: String): Long? {
val match = SIZE_UNITS_REGEX.matchEntire(input.trim()) ?: return null
val (value, _, unit) = match.destructured
val factor = SIZE_UNITS[unit.uppercase()] ?: return null
return (value.toDoubleOrNull()?.times(factor))?.toLong()
}

private val dialogLayout = ViewPreferenceInputSizeBinding.inflate(activity.layoutInflater, null, false).apply {
slider.valueFrom = minimumSize.toFloat()
slider.valueTo = (maximumSize / KB_MULTIPLIER).toFloat()

fun setSliderSize(size: Long) {
slider.value = (size.coerceAtMost(maximumSize) / KB_MULTIPLIER)
.coerceAtLeast(minimumSize / KB_MULTIPLIER)
.toFloat()
}

fun getSliderSize(): Long = slider.value.toLong() * KB_MULTIPLIER
fun setSizeText(size: Long): Unit = sizeText.setText(Formatter.formatShortFileSize(context, size))

setSliderSize(currentSize)
setSizeText(currentSize)

slider.setLabelFormatter { Formatter.formatShortFileSize(context, getSliderSize()) }

sizeText.addTextChangedListener {
parseSize(it.toString())?.let { setSliderSize(it) }
}

slider.addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
override fun onStartTrackingTouch(slider: Slider) {
sizeText.clearFocus()
setSizeText(getSliderSize())
}

override fun onStopTrackingTouch(slider: Slider) {
setSizeText(getSliderSize())
}
})
}
private val dialog = MaterialAlertDialogBuilder(context).apply {
setTitle(titleRes)
setView(dialogLayout.root)
setPositiveButton(eu.darken.sdmse.common.R.string.general_save_action) { _, _ ->
onSave(dialogLayout.slider.value.toLong() * KB_MULTIPLIER)
}
setNegativeButton(eu.darken.sdmse.common.R.string.general_cancel_action) { _, _ ->
onCancel()
}
setNeutralButton(eu.darken.sdmse.common.R.string.general_reset_action) { _, _ ->
onReset()
}
}

fun show() {
dialog.show()
}

companion object {
private const val KB_MULTIPLIER = 1024L
private val SIZE_UNITS_REGEX = Regex("(\\d+(\\.\\d+)?)\\s*(B|KB|MB|GB)", RegexOption.IGNORE_CASE)
private val SIZE_UNITS = mapOf(
"B" to 1L,
"KB" to 1_000L,
"MB" to 1_000_000L,
"GB" to 1_000_000_000L,
)
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
package eu.darken.sdmse.deduplicator.ui.settings

import android.text.format.Formatter
import androidx.annotation.Keep
import androidx.fragment.app.viewModels
import androidx.preference.Preference
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.slider.Slider
import dagger.hilt.android.AndroidEntryPoint
import eu.darken.sdmse.R
import eu.darken.sdmse.appcleaner.ui.settings.SizeInputDialog
import eu.darken.sdmse.common.datastore.valueBlocking
import eu.darken.sdmse.common.uix.PreferenceFragment2
import eu.darken.sdmse.databinding.ViewPreferenceSeekbarBinding
import eu.darken.sdmse.deduplicator.core.DeduplicatorSettings
import javax.inject.Inject

Expand All @@ -30,40 +27,13 @@ class DeduplicatorSettingsFragment : PreferenceFragment2() {

findPreference<Preference>(settings.minSizeBytes.keyName)?.apply {
setOnPreferenceClickListener {
val dialogLayout = ViewPreferenceSeekbarBinding.inflate(layoutInflater, null, false)
dialogLayout.apply {
slider.valueFrom = 0f
slider.valueTo = 100 * 1024f
slider.value = (settings.minSizeBytes.valueBlocking / 1024f).coerceAtMost(slider.valueTo)

val getSliderText = { value: Float ->
val size = value.toLong() * 1024L
Formatter.formatShortFileSize(requireContext(), size)
}
slider.setLabelFormatter { getSliderText(it) }
sliderValue.text = getSliderText(slider.value)

slider.addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
override fun onStartTrackingTouch(slider: Slider) {
sliderValue.text = getSliderText(slider.value)
}

override fun onStopTrackingTouch(slider: Slider) {
sliderValue.text = getSliderText(slider.value)
}
})
}
MaterialAlertDialogBuilder(requireContext()).apply {
setTitle(R.string.deduplicator_skip_minsize_title)
setView(dialogLayout.root)
setPositiveButton(eu.darken.sdmse.common.R.string.general_save_action) { _, _ ->
settings.minSizeBytes.valueBlocking = dialogLayout.slider.value.toLong() * 1024L
}
setNegativeButton(eu.darken.sdmse.common.R.string.general_cancel_action) { _, _ -> }
setNeutralButton(eu.darken.sdmse.common.R.string.general_reset_action) { _, _ ->
settings.minSizeBytes.valueBlocking = DeduplicatorSettings.MIN_FILE_SIZE
}
}.show()
SizeInputDialog(
requireActivity(),
titleRes = R.string.deduplicator_skip_minsize_title,
currentSize = settings.minSizeBytes.valueBlocking,
onReset = { settings.minSizeBytes.valueBlocking = DeduplicatorSettings.MIN_FILE_SIZE },
onSave = { settings.minSizeBytes.valueBlocking = it }
).show()
true
}
}
Expand Down
33 changes: 33 additions & 0 deletions app/src/main/res/layout/view_preference_input_size.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<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:padding="16dp">

<com.google.android.material.textfield.TextInputEditText
android:id="@+id/size_text"
style="@style/ThemeOverlay.Material3.TextInputEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:ems="6"
android:gravity="center"
app:layout_constraintBottom_toTopOf="@id/slider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="100.00 MB" />

<com.google.android.material.slider.Slider
android:id="@+id/slider"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:contentDescription="Slider for minimum app junk size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/size_text"
tools:ignore="HardcodedText" />
</androidx.constraintlayout.widget.ConstraintLayout>

1 comment on commit e9fb92a

@PRuHADen
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice

Please sign in to comment.