fix(preferences): show confirmation buttons in dialog preferences

This commit is contained in:
Akash Yadav
2023-11-05 01:14:15 +05:30
parent ea8deab5d1
commit 624e55a2c9
8 changed files with 190 additions and 67 deletions
@@ -46,6 +46,7 @@ import com.itsaky.androidide.tasks.executeAsync
import com.itsaky.androidide.utils.Environment.GRADLE_USER_HOME
import com.itsaky.androidide.utils.flashError
import com.itsaky.androidide.utils.flashSuccess
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import java.io.File
@@ -112,15 +113,18 @@ private class GradleCommands(
)
}
override fun onItemSelected(position: Int, isSelected: Boolean) {
when (position) {
0 -> isStacktraceEnabled = isSelected
1 -> isInfoEnabled = isSelected
2 -> isDebugEnabled = isSelected
3 -> isScanEnabled = isSelected
4 -> isWarningModeAllEnabled = isSelected
5 -> isBuildCacheEnabled = isSelected
6 -> isOfflineEnabled = isSelected
override fun onChoicesConfirmed(selectedPositions: List<Int>) {
for (position in selectedPositions) {
when (position) {
0 -> ::isStacktraceEnabled
1 -> ::isInfoEnabled
2 -> ::isDebugEnabled
3 -> ::isScanEnabled
4 -> ::isWarningModeAllEnabled
5 -> ::isBuildCacheEnabled
6 -> ::isOfflineEnabled
else -> null
}?.set(true)
}
}
}
@@ -183,21 +183,21 @@ private class TabSize(
return choices
}
override fun onItemSelected(position: Int, isSelected: Boolean) {
var size = (position + 1) * 2
if (size < 2 || size > 8) {
size = 4
}
tabSize = size
}
override fun getSelectedItem(context: Context): Int {
override fun getInitiallySelectionItemPosition(context: Context): Int {
var current = tabSize / 2 - 1
if (current < 0 || current >= choices.size) {
current = 1
}
return current
}
override fun onChoiceConfirmed(position: Int) {
var size = (position + 1) * 2
if (size < 2 || size > 8) {
size = 4
}
tabSize = size
}
}
@Parcelize
@@ -217,14 +217,12 @@ private class ColorSchemePreference(
return schemes.map { it.name }.toTypedArray()
}
override fun getSelectedItem(context: Context): Int {
override fun getInitiallySelectionItemPosition(context: Context): Int {
return schemes.indexOfFirst { it.key == colorScheme }
}
override fun onItemSelected(position: Int, isSelected: Boolean) {
if (isSelected) {
colorScheme = schemes[position].key
}
override fun onChoiceConfirmed(position: Int) {
colorScheme = schemes[position].key
}
}
@@ -240,16 +238,6 @@ private class NonPrintablePaintingFlags(
return arrayOf("Leading", "Trailing", "Inner", "Empty lines", "Line breaks")
}
override fun onItemSelected(position: Int, isSelected: Boolean) {
when (position) {
0 -> drawLeadingWs = isSelected
1 -> drawTrailingWs = isSelected
2 -> drawInnerWs = isSelected
3 -> drawEmptyLineWs = isSelected
4 -> drawLineBreak = isSelected
}
}
override fun getCheckedItems(): BooleanArray {
return booleanArrayOf(
drawLeadingWs,
@@ -259,6 +247,19 @@ private class NonPrintablePaintingFlags(
drawLineBreak
)
}
override fun onChoicesConfirmed(selectedPositions: List<Int>) {
for (position in selectedPositions) {
when (position) {
0 -> ::drawLeadingWs
1 -> ::drawTrailingWs
2 -> ::drawInnerWs
3 -> ::drawEmptyLineWs
4 -> ::drawLineBreak
else -> null
}?.set(true)
}
}
}
@Parcelize
@@ -110,7 +110,7 @@ class UiMode(
)
}
override fun getSelectedItem(context: Context): Int {
override fun getInitiallySelectionItemPosition(context: Context): Int {
return when (uiMode) {
AppCompatDelegate.MODE_NIGHT_NO -> 0
AppCompatDelegate.MODE_NIGHT_YES -> 1
@@ -118,15 +118,11 @@ class UiMode(
}
}
override fun onItemSelected(position: Int, isSelected: Boolean) {
if (isSelected) {
val mode =
when (position) {
0 -> AppCompatDelegate.MODE_NIGHT_NO
1 -> AppCompatDelegate.MODE_NIGHT_YES
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
uiMode = mode
override fun onChoiceConfirmed(position: Int) {
uiMode = when (position) {
0 -> AppCompatDelegate.MODE_NIGHT_NO
1 -> AppCompatDelegate.MODE_NIGHT_YES
else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
}
}
@@ -146,14 +142,12 @@ class ThemeSelector(
return themes.map { context.getString(it.title) }.toTypedArray()
}
override fun getSelectedItem(context: Context): Int {
override fun getInitiallySelectionItemPosition(context: Context): Int {
return themes.indexOf(ThemeManager.getCurrentTheme())
}
override fun onItemSelected(position: Int, isSelected: Boolean) {
if (isSelected) {
selectedTheme = themes[position].name
}
override fun onChoiceConfirmed(position: Int) {
selectedTheme = themes[position].name
}
}
@@ -227,7 +227,7 @@ private class EmptyElementsBehavior(
) : SingleChoicePreference() {
override val dialogCancellable = true
override fun getSelectedItem(context: Context): Int {
override fun getInitiallySelectionItemPosition(context: Context): Int {
return EmptyElements.valueOf(emptyElementsBehavior).ordinal
}
@@ -235,7 +235,7 @@ private class EmptyElementsBehavior(
return EmptyElements.values().map { it.toString() }.toTypedArray()
}
override fun onItemSelected(position: Int, isSelected: Boolean) {
override fun onChoiceConfirmed(position: Int) {
emptyElementsBehavior = EmptyElements.values()[position].toString()
}
}
@@ -0,0 +1,73 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.preferences
import androidx.preference.Preference
import com.google.android.material.dialog.MaterialAlertDialogBuilder
/**
* Base class for dialog preferences which allows users to choose from multiple items.
*
* @author Akash Yadav
*/
abstract class ChoiceBasedDialogPreference : DialogPreference(), PreferenceChoices {
protected open var selectedPositions: MutableList<Int>? = null
final override fun onConfigureDialog(preference: Preference, dialog: MaterialAlertDialogBuilder) {
val choices = getChoices(preference.context)
selectedPositions = MutableList(size = choices.size) { -1 }
onConfigureDialogChoices(preference, dialog, choices)
dialog.setPositiveButton(android.R.string.ok) { dialogInterface, _ ->
dialogInterface.dismiss()
val positions = selectedPositions?.also { positions ->
positions.removeIf { idx -> idx == -1 }
}
onChoicesConfirmed(positions ?: emptyList())
}
dialog.setNegativeButton(android.R.string.cancel) { dialogInterface, _ ->
dialogInterface.dismiss()
onChoicesCancelled()
}
}
override fun onSelectionChanged(position: Int, isSelected: Boolean) {
if (isSelected) {
selectedPositions!!.add(position)
} else {
selectedPositions!!.removeAt(position)
}
}
/**
* Configure the dialog choices.
*/
protected abstract fun onConfigureDialogChoices(
preference: Preference,
dialog: MaterialAlertDialogBuilder,
choices: Array<String>
)
override fun onChoicesConfirmed(selectedPositions: List<Int>) {}
override fun onChoicesCancelled() {}
}
@@ -23,21 +23,32 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
/**
* A preference with multiple choices to select from.
*
* The [onSelectionChanged] is called whenever the
* [DialogInterface.OnMultiChoiceClickListener][android.content.DialogInterface.OnMultiChoiceClickListener]
* is called.
*
* @author Akash Yadav
*/
abstract class MultiChoicePreference : DialogPreference(), PreferenceChoices {
abstract class MultiChoicePreference : ChoiceBasedDialogPreference(), PreferenceChoices {
/**
* Get the index of all the items that should be selected by default.
* Get the index of all the checked and unchecked items.
*
* @see MaterialAlertDialogBuilder.setMultiChoiceItems
*/
abstract fun getCheckedItems(): BooleanArray
override fun onConfigureDialog(preference: Preference, dialog: MaterialAlertDialogBuilder) {
super.onConfigureDialog(preference, dialog)
dialog.setMultiChoiceItems(getChoices(preference.context), getCheckedItems()) { _, which, checked ->
onItemSelected(which, checked)
override fun onConfigureDialogChoices(
preference: Preference,
dialog: MaterialAlertDialogBuilder,
choices: Array<String>
) {
dialog.setMultiChoiceItems(
getChoices(preference.context),
getCheckedItems()
) { _, which, checked ->
onSelectionChanged(which, checked)
}
dialog.setPositiveButton(android.R.string.ok, null)
}
}
@@ -28,11 +28,24 @@ interface PreferenceChoices {
/** Get the choices to show in the preference. */
fun getChoices(context: Context): Array<String>
/**
* Called when an item is selected from the single choice list.
*
* @param position The position of the selected item.
* @param isSelected Whether the item is selected.
*/
fun onItemSelected(position: Int, isSelected: Boolean = true)
fun onSelectionChanged(position: Int, isSelected: Boolean)
/**
* Called when the user confirms the selections.
*
* @param selectedPositions The positions of the selected items.
*/
fun onChoicesConfirmed(selectedPositions: List<Int>)
/**
* Called when the user cancels the selections.
*/
fun onChoicesCancelled()
}
@@ -24,22 +24,49 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
/**
* A preference which allows selecting a single value from a list of values.
*
* The [onSelectionChanged] method is called exactly two times when the user changes the selection, first call for the previously
* selected item and second call for the newly selected item.
*
* The [onChoicesConfirmed] is always called with a singleton list.
*
* @author Akash Yadav
*/
abstract class SingleChoicePreference : DialogPreference(), PreferenceChoices {
abstract class SingleChoicePreference : ChoiceBasedDialogPreference(), PreferenceChoices {
/**
* The currently selected item in the dialog.
*/
protected open var currentSelection: Int = -1
/**
* Get the index of the selected item.
* @see MaterialAlertDialogBuilder.setSingleChoiceItems
*/
abstract fun getSelectedItem(context: Context): Int
abstract fun getInitiallySelectionItemPosition(context: Context): Int
override fun onConfigureDialog(preference: Preference, dialog: MaterialAlertDialogBuilder) {
super.onConfigureDialog(preference, dialog)
dialog.setSingleChoiceItems(getChoices(preference.context),
getSelectedItem(preference.context)) { dialogInterface, position ->
dialogInterface.dismiss()
onItemSelected(position)
override fun onConfigureDialogChoices(
preference: Preference,
dialog: MaterialAlertDialogBuilder,
choices: Array<String>
) {
dialog.setSingleChoiceItems(
getChoices(preference.context),
getInitiallySelectionItemPosition(preference.context))
{ _, position ->
if (currentSelection != -1) {
onSelectionChanged(currentSelection, false)
}
currentSelection = position
onSelectionChanged(position, true)
}
}
final override fun onChoicesConfirmed(selectedPositions: List<Int>) {
selectedPositions.firstOrNull()?.let { onChoiceConfirmed(it) }
}
protected open fun onChoiceConfirmed(position: Int) {}
}