From e5448a4a19cd1c4be22d20afd25d34577562f1ca Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Fri, 10 Nov 2023 14:29:25 +0530 Subject: [PATCH] fix(preferences): add selection handler logic for choice prefs (fixes #1444) --- .../androidide/preferences/buildAndRun.kt | 48 +++++++------------ .../itsaky/androidide/preferences/editor.kt | 6 ++- .../itsaky/androidide/preferences/xmlPrefs.kt | 3 ++ .../ChoiceBasedDialogPreference.kt | 29 ++++++----- .../preferences/MultiChoicePreference.kt | 14 ++---- .../preferences/PreferenceChoices.kt | 10 +++- .../preferences/SingleChoicePreference.kt | 16 +++++-- 7 files changed, 66 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/com/itsaky/androidide/preferences/buildAndRun.kt b/app/src/main/java/com/itsaky/androidide/preferences/buildAndRun.kt index 3c7a6fcfc..a0f48a47e 100644 --- a/app/src/main/java/com/itsaky/androidide/preferences/buildAndRun.kt +++ b/app/src/main/java/com/itsaky/androidide/preferences/buildAndRun.kt @@ -89,42 +89,28 @@ private class GradleCommands( override val icon: Int? = drawable.ic_bash_commands, ) : MultiChoicePreference() { - override fun getCheckedItems(): BooleanArray { - return booleanArrayOf( - isStacktraceEnabled, - isInfoEnabled, - isDebugEnabled, - isScanEnabled, - isWarningModeAllEnabled, - isBuildCacheEnabled, - isOfflineEnabled - ) + @IgnoredOnParcel + private val choices = linkedMapOf( + "--stacktrace" to ::isStacktraceEnabled, + "--info" to ::isInfoEnabled, + "--debug" to ::isDebugEnabled, + "--scan" to ::isScanEnabled, + "--warning-mode all" to ::isWarningModeAllEnabled, + "--build-cache" to ::isBuildCacheEnabled, + "--offline" to ::isOfflineEnabled, + ) + + override fun getCheckedItems(choices: Array): BooleanArray { + return BooleanArray(choices.size) { this.choices[choices[it]]?.get() == true } } override fun getChoices(context: Context): Array { - return arrayOf( - "--stacktrace", - "--info", - "--debug", - "--scan", - "--warning-mode all", - "--build-cache", - "--offline" - ) + return choices.keys.toTypedArray() } - override fun onChoicesConfirmed(selectedPositions: List) { - for (position in selectedPositions) { - when (position) { - 0 -> ::isStacktraceEnabled - 1 -> ::isInfoEnabled - 2 -> ::isDebugEnabled - 3 -> ::isScanEnabled - 4 -> ::isWarningModeAllEnabled - 5 -> ::isBuildCacheEnabled - 6 -> ::isOfflineEnabled - else -> null - }?.set(true) + override fun onChoicesConfirmed(selectedPositions: IntArray, selections: Map) { + selections.forEach { (key, value) -> + choices[key]?.set(value) } } } diff --git a/app/src/main/java/com/itsaky/androidide/preferences/editor.kt b/app/src/main/java/com/itsaky/androidide/preferences/editor.kt index 75ab07687..a59fbb2fc 100644 --- a/app/src/main/java/com/itsaky/androidide/preferences/editor.kt +++ b/app/src/main/java/com/itsaky/androidide/preferences/editor.kt @@ -174,6 +174,7 @@ private class TabSize( override val icon: Int? = drawable.ic_font_ligatures, ) : SingleChoicePreference() { + @IgnoredOnParcel override val dialogCancellable = true @IgnoredOnParcel @@ -208,6 +209,7 @@ private class ColorSchemePreference( override val icon: Int? = R.drawable.ic_color_scheme ) : SingleChoicePreference() { + @IgnoredOnParcel override val dialogCancellable = true @IgnoredOnParcel @@ -238,7 +240,7 @@ private class NonPrintablePaintingFlags( return arrayOf("Leading", "Trailing", "Inner", "Empty lines", "Line breaks") } - override fun getCheckedItems(): BooleanArray { + override fun getCheckedItems(choices: Array): BooleanArray? { return booleanArrayOf( drawLeadingWs, drawTrailingWs, @@ -248,7 +250,7 @@ private class NonPrintablePaintingFlags( ) } - override fun onChoicesConfirmed(selectedPositions: List) { + override fun onChoicesConfirmed(selectedPositions: IntArray, selections: Map) { for (position in selectedPositions) { when (position) { 0 -> ::drawLeadingWs diff --git a/app/src/main/java/com/itsaky/androidide/preferences/xmlPrefs.kt b/app/src/main/java/com/itsaky/androidide/preferences/xmlPrefs.kt index 79354062e..3f33a53ae 100644 --- a/app/src/main/java/com/itsaky/androidide/preferences/xmlPrefs.kt +++ b/app/src/main/java/com/itsaky/androidide/preferences/xmlPrefs.kt @@ -49,6 +49,7 @@ import com.itsaky.androidide.preferences.internal.splitAttributes import com.itsaky.androidide.preferences.internal.splitAttributesIndentSize import com.itsaky.androidide.preferences.internal.trimFinalNewLine import com.itsaky.androidide.preferences.internal.trimTrailingWhitespace +import kotlinx.parcelize.IgnoredOnParcel import org.eclipse.lemminx.dom.builder.EmptyElements import kotlinx.parcelize.Parcelize @@ -225,6 +226,8 @@ private class EmptyElementsBehavior( override val title: Int = string.idepref_emptyElements_title, override val summary: Int? = string.idepref_emptyElements_summary ) : SingleChoicePreference() { + + @IgnoredOnParcel override val dialogCancellable = true override fun getInitiallySelectionItemPosition(context: Context): Int { diff --git a/preferences/src/main/java/com/itsaky/androidide/preferences/ChoiceBasedDialogPreference.kt b/preferences/src/main/java/com/itsaky/androidide/preferences/ChoiceBasedDialogPreference.kt index a3b51abdd..2a47fab12 100644 --- a/preferences/src/main/java/com/itsaky/androidide/preferences/ChoiceBasedDialogPreference.kt +++ b/preferences/src/main/java/com/itsaky/androidide/preferences/ChoiceBasedDialogPreference.kt @@ -27,21 +27,25 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder */ abstract class ChoiceBasedDialogPreference : DialogPreference(), PreferenceChoices { - protected open var selectedPositions: MutableList? = null + private var checkedPositions = intArrayOf() final override fun onConfigureDialog(preference: Preference, dialog: MaterialAlertDialogBuilder) { val choices = getChoices(preference.context) - selectedPositions = MutableList(size = choices.size) { -1 } + val checkedItems = getCheckedItems(choices) - onConfigureDialogChoices(preference, dialog, choices) + checkedPositions = checkedItems?.map { if(it) 1 else 0 }?.toIntArray() ?: intArrayOf() + + onConfigureDialogChoices(preference, dialog, choices, checkedItems) dialog.setPositiveButton(android.R.string.ok) { dialogInterface, _ -> dialogInterface.dismiss() - val positions = selectedPositions?.also { positions -> - positions.removeIf { idx -> idx == -1 } + val selections = HashMap(choices.size) + for (i in 0..checkedPositions.lastIndex) { + selections[choices[i]] = checkedPositions[i] == 1 } - onChoicesConfirmed(positions ?: emptyList()) + + onChoicesConfirmed(checkedPositions, selections) } dialog.setNegativeButton(android.R.string.cancel) { dialogInterface, _ -> @@ -51,11 +55,11 @@ abstract class ChoiceBasedDialogPreference : DialogPreference(), PreferenceChoic } override fun onSelectionChanged(position: Int, isSelected: Boolean) { - if (isSelected) { - selectedPositions!!.add(position) - } else { - selectedPositions!!.removeAt(position) + if (position < 0 || position >= checkedPositions.size) { + return } + + checkedPositions[position] = if (isSelected) 1 else 0 } /** @@ -64,10 +68,11 @@ abstract class ChoiceBasedDialogPreference : DialogPreference(), PreferenceChoic protected abstract fun onConfigureDialogChoices( preference: Preference, dialog: MaterialAlertDialogBuilder, - choices: Array + choices: Array, + checkedItems: BooleanArray? ) - override fun onChoicesConfirmed(selectedPositions: List) {} + override fun onChoicesConfirmed(selectedPositions: IntArray, selections: Map) {} override fun onChoicesCancelled() {} } \ No newline at end of file diff --git a/preferences/src/main/java/com/itsaky/androidide/preferences/MultiChoicePreference.kt b/preferences/src/main/java/com/itsaky/androidide/preferences/MultiChoicePreference.kt index 6cf834d7b..56d94cc6f 100644 --- a/preferences/src/main/java/com/itsaky/androidide/preferences/MultiChoicePreference.kt +++ b/preferences/src/main/java/com/itsaky/androidide/preferences/MultiChoicePreference.kt @@ -31,22 +31,16 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder */ abstract class MultiChoicePreference : ChoiceBasedDialogPreference(), PreferenceChoices { - /** - * Get the index of all the checked and unchecked items. - * - * @see MaterialAlertDialogBuilder.setMultiChoiceItems - */ - abstract fun getCheckedItems(): BooleanArray - override fun onConfigureDialogChoices( preference: Preference, dialog: MaterialAlertDialogBuilder, - choices: Array + choices: Array, + checkedItems: BooleanArray? ) { dialog.setMultiChoiceItems( - getChoices(preference.context), - getCheckedItems() + choices, + checkedItems!! ) { _, which, checked -> onSelectionChanged(which, checked) } diff --git a/preferences/src/main/java/com/itsaky/androidide/preferences/PreferenceChoices.kt b/preferences/src/main/java/com/itsaky/androidide/preferences/PreferenceChoices.kt index f682d740a..b9afbbbd7 100644 --- a/preferences/src/main/java/com/itsaky/androidide/preferences/PreferenceChoices.kt +++ b/preferences/src/main/java/com/itsaky/androidide/preferences/PreferenceChoices.kt @@ -18,6 +18,7 @@ package com.itsaky.androidide.preferences import android.content.Context +import com.google.android.material.dialog.MaterialAlertDialogBuilder /** * A preference with choices. @@ -29,6 +30,13 @@ interface PreferenceChoices { /** Get the choices to show in the preference. */ fun getChoices(context: Context): Array + /** + * Get all checked and unchecked items. + * + * @see MaterialAlertDialogBuilder.setMultiChoiceItems + */ + fun getCheckedItems(choices: Array): BooleanArray? + /** * Called when an item is selected from the single choice list. * @@ -42,7 +50,7 @@ interface PreferenceChoices { * * @param selectedPositions The positions of the selected items. */ - fun onChoicesConfirmed(selectedPositions: List) + fun onChoicesConfirmed(selectedPositions: IntArray, selections: Map) /** * Called when the user cancels the selections. diff --git a/preferences/src/main/java/com/itsaky/androidide/preferences/SingleChoicePreference.kt b/preferences/src/main/java/com/itsaky/androidide/preferences/SingleChoicePreference.kt index c2e5c22aa..63ed11d06 100644 --- a/preferences/src/main/java/com/itsaky/androidide/preferences/SingleChoicePreference.kt +++ b/preferences/src/main/java/com/itsaky/androidide/preferences/SingleChoicePreference.kt @@ -44,14 +44,19 @@ abstract class SingleChoicePreference : ChoiceBasedDialogPreference(), Preferenc */ abstract fun getInitiallySelectionItemPosition(context: Context): Int + final override fun getCheckedItems(choices: Array): BooleanArray? { + return null + } + override fun onConfigureDialogChoices( preference: Preference, dialog: MaterialAlertDialogBuilder, - choices: Array + choices: Array, + checkedItems: BooleanArray? ) { dialog.setSingleChoiceItems( - getChoices(preference.context), + choices, getInitiallySelectionItemPosition(preference.context)) { _, position -> @@ -64,8 +69,11 @@ abstract class SingleChoicePreference : ChoiceBasedDialogPreference(), Preferenc } } - final override fun onChoicesConfirmed(selectedPositions: List) { - selectedPositions.firstOrNull()?.let { onChoiceConfirmed(it) } + final override fun onChoicesConfirmed( + selectedPositions: IntArray, + selections: Map + ) { + onChoiceConfirmed(currentSelection) } protected open fun onChoiceConfirmed(position: Int) {}