diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/reviewer/MappableBinding.kt b/AnkiDroid/src/main/java/com/ichi2/anki/reviewer/MappableBinding.kt index 863e9c4a114b..1f6dd2f98a60 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/reviewer/MappableBinding.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/reviewer/MappableBinding.kt @@ -17,12 +17,10 @@ package com.ichi2.anki.reviewer import android.content.Context -import android.content.SharedPreferences import androidx.annotation.CheckResult import com.ichi2.anki.R import com.ichi2.anki.cardviewer.Gesture import com.ichi2.anki.cardviewer.ScreenAction -import com.ichi2.anki.cardviewer.ViewerCommand import com.ichi2.anki.reviewer.Binding.AxisButtonBinding import com.ichi2.anki.reviewer.Binding.GestureInput import com.ichi2.anki.reviewer.Binding.KeyBinding @@ -104,23 +102,6 @@ sealed class MappableBinding( .mapNotNull { it.toPreferenceString() } .joinToString(prefix = VERSION_PREFIX, separator = PREF_SEPARATOR.toString()) - @CheckResult - fun fromString(s: String): MappableBinding? { - if (s.isEmpty()) { - return null - } - return try { - // the prefix of the serialized - when (s[0]) { - 'r' -> ReviewerBinding.fromString(s.substring(1)) - else -> null - } - } catch (e: Exception) { - Timber.w(e, "failed to deserialize binding") - null - } - } - @CheckResult fun getPreferenceBindingStrings(string: String): List { if (string.isEmpty()) return emptyList() @@ -130,30 +111,6 @@ sealed class MappableBinding( } return string.substring(VERSION_PREFIX.length).split(PREF_SEPARATOR).filter { it.isNotEmpty() } } - - @CheckResult - fun fromPreferenceString(string: String?): MutableList { - if (string.isNullOrEmpty()) return ArrayList() - try { - val version = string.takeWhile { x -> x != '/' } - val remainder = string.substring(version.length + 1) // skip the / - if (version != "1") { - Timber.w("cannot handle version '$version'") - return ArrayList() - } - return remainder.split(PREF_SEPARATOR).mapNotNull { fromString(it) }.toMutableList() - } catch (e: Exception) { - Timber.w(e, "Failed to deserialize preference") - return ArrayList() - } - } - - @CheckResult - fun allMappings(prefs: SharedPreferences): MutableList>> = - ViewerCommand.entries - .map { - Pair(it, it.getBindings(prefs).toMutableList()) - }.toMutableList() } } diff --git a/AnkiDroid/src/main/java/com/ichi2/preferences/ControlPreference.kt b/AnkiDroid/src/main/java/com/ichi2/preferences/ControlPreference.kt index 619fe10409e3..f6e7d382ce91 100644 --- a/AnkiDroid/src/main/java/com/ichi2/preferences/ControlPreference.kt +++ b/AnkiDroid/src/main/java/com/ichi2/preferences/ControlPreference.kt @@ -1,5 +1,6 @@ /* * Copyright (c) 2021 David Allison + * Copyright (c) 2024 Brayan Oliveira * * This program 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 @@ -13,49 +14,52 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ - package com.ichi2.preferences -import android.annotation.SuppressLint +import android.app.Dialog import android.content.Context -import android.content.DialogInterface +import android.os.Bundle +import android.text.TextUtils import android.util.AttributeSet +import android.view.View +import android.widget.ArrayAdapter +import android.widget.LinearLayout +import android.widget.ListView import androidx.appcompat.app.AlertDialog -import androidx.preference.ListPreference +import androidx.core.view.isVisible +import androidx.fragment.app.DialogFragment +import androidx.preference.DialogPreference +import androidx.preference.PreferenceFragmentCompat import com.ichi2.anki.R +import com.ichi2.anki.cardviewer.Gesture import com.ichi2.anki.cardviewer.GestureProcessor -import com.ichi2.anki.cardviewer.ViewerCommand -import com.ichi2.anki.dialogs.CardSideSelectionDialog import com.ichi2.anki.dialogs.GestureSelectionDialogUtils import com.ichi2.anki.dialogs.GestureSelectionDialogUtils.onGestureChanged import com.ichi2.anki.dialogs.KeySelectionDialogUtils import com.ichi2.anki.dialogs.WarningDisplay -import com.ichi2.anki.preferences.sharedPrefs -import com.ichi2.anki.reviewer.CardSide +import com.ichi2.anki.preferences.SettingsFragment +import com.ichi2.anki.preferences.requirePreference +import com.ichi2.anki.reviewer.Binding import com.ichi2.anki.reviewer.MappableBinding import com.ichi2.anki.reviewer.MappableBinding.Companion.toPreferenceString -import com.ichi2.anki.reviewer.ReviewerBinding -import com.ichi2.anki.showThemedToast +import com.ichi2.anki.utils.ext.sharedPrefs import com.ichi2.ui.AxisPicker import com.ichi2.ui.KeyPicker +import com.ichi2.utils.create import com.ichi2.utils.customView -import com.ichi2.utils.message import com.ichi2.utils.negativeButton import com.ichi2.utils.positiveButton import com.ichi2.utils.show -import com.ichi2.utils.title /** * A preference which allows mapping of inputs to actions (example: keys -> commands) * * This is implemented as a List, the elements allow the user to either add, or * remove previously mapped keys - * - * Future: - * * Allow mapping gestures here - * * Allow maps other than the reviewer */ -class ControlPreference : ListPreference { +abstract class ControlPreference : + DialogPreference, + DialogFragmentProvider { @Suppress("unused") constructor( context: Context, @@ -73,221 +77,165 @@ class ControlPreference : ListPreference { @Suppress("unused") constructor(context: Context) : super(context) - private fun refreshEntries() { - val entryTitles: MutableList = ArrayList() - val entryIndices: MutableList = ArrayList() - // negative indices are "add" - entryTitles.add(context.getString(R.string.binding_add_key)) - entryIndices.add(ADD_KEY_INDEX) - // Add a joystick/motion controller - entryTitles.add(context.getString(R.string.binding_add_axis)) - entryIndices.add(ADD_AXIS_INDEX) - // Put "Add gesture" option if gestures are enabled - if (context.sharedPrefs().getBoolean(GestureProcessor.PREF_KEY, false)) { - entryTitles.add(context.getString(R.string.binding_add_gesture)) - entryIndices.add(ADD_GESTURE_INDEX) - } - // 0 and above are "delete" actions for already mapped preferences - for ((i, binding) in MappableBinding.fromPreferenceString(value).withIndex()) { - entryTitles.add(context.getString(R.string.binding_remove_binding, binding.toDisplayString(context))) - entryIndices.add(i) - } - entries = entryTitles.toTypedArray() - entryValues = entryIndices.map { it.toString() }.toTypedArray() - } + abstract fun getMappableBindings(): List - override fun onClick() { - refreshEntries() - super.onClick() - } + abstract fun onKeySelected(binding: Binding) - /** The summary that appears on the preference */ - override fun getSummary(): CharSequence = - MappableBinding - .fromPreferenceString(value) - .joinToString(", ") { it.toDisplayString(context) } + abstract fun onAxisSelected(binding: Binding) - /** Called when an element is selected in the ListView */ - @SuppressLint("CheckResult") - override fun callChangeListener(newValue: Any?): Boolean { - when (val index: Int = (newValue as String).toInt()) { - ADD_GESTURE_INDEX -> { - val actionName = title - AlertDialog.Builder(context).show { - title(text = actionName.toString()) + abstract val areGesturesEnabled: Boolean - val gesturePicker = GestureSelectionDialogUtils.getGesturePicker(context) + protected open fun onGestureSelected(gesture: Gesture) {} - positiveButton(R.string.dialog_ok) { - val gesture = gesturePicker.getGesture() ?: return@positiveButton - val mappableBinding = ReviewerBinding.fromGesture(gesture) - if (bindingIsUsedOnAnotherCommand(mappableBinding)) { - showDialogToReplaceBinding(mappableBinding, context.getString(R.string.binding_replace_gesture), it) - } else { - addBinding(mappableBinding) - it.dismiss() - } - } - negativeButton(R.string.dialog_cancel) { it.dismiss() } - customView(view = gesturePicker) + /** @return whether the binding is used in another action */ + abstract fun warnIfUsed( + binding: Binding, + warningDisplay: WarningDisplay?, + ): Boolean - gesturePicker.onGestureChanged { gesture -> - warnIfBindingIsUsed(ReviewerBinding.fromGesture(gesture), gesturePicker) - } - } - } - ADD_KEY_INDEX -> { - val actionName = title - AlertDialog.Builder(context).show { - val keyPicker: KeyPicker = KeyPicker.inflate(context) - customView(view = keyPicker.rootLayout) - title(text = actionName.toString()) + fun getValue(): String? = getPersistedString(null) + + fun setValue(value: String) { + if (!TextUtils.equals(getValue(), value)) { + persistString(value) + notifyChanged() + } + } - // When the user presses a key - keyPicker.setBindingChangedListener { binding -> - val mappableBinding = ReviewerBinding(binding, CardSide.BOTH) - warnIfBindingIsUsed(mappableBinding, keyPicker) - } + override fun getSummary(): CharSequence = getMappableBindings().joinToString(", ") { it.toDisplayString(context) } - positiveButton(R.string.dialog_ok) { - val binding = keyPicker.getBinding() ?: return@positiveButton - // Use CardSide.BOTH as placeholder just to check if binding exists - CardSideSelectionDialog.displayInstance(context) { side -> - val mappableBinding = ReviewerBinding(binding, side) - if (bindingIsUsedOnAnotherCommand(mappableBinding)) { - showDialogToReplaceBinding(mappableBinding, context.getString(R.string.binding_replace_key), it) - } else { - addBinding(mappableBinding) - it.dismiss() - } - } - } - negativeButton(R.string.dialog_cancel) { it.dismiss() } + override fun makeDialogFragment(): DialogFragment = ControlPreferenceDialogFragment() - keyPicker.setKeycodeValidation(KeySelectionDialogUtils.disallowModifierKeyCodes()) - } + fun showGesturePickerDialog() { + AlertDialog.Builder(context).show { + setTitle(title) + setIcon(icon) + val gesturePicker = GestureSelectionDialogUtils.getGesturePicker(context) + positiveButton(R.string.dialog_ok) { + val gesture = gesturePicker.getGesture() ?: return@positiveButton + onGestureSelected(gesture) + it.dismiss() } - ADD_AXIS_INDEX -> displayAddAxisDialog() - else -> { - val bindings: MutableList = MappableBinding.fromPreferenceString(value) - bindings.removeAt(index) - value = bindings.toPreferenceString() + negativeButton(R.string.dialog_cancel) { it.dismiss() } + customView(view = gesturePicker) + gesturePicker.onGestureChanged { gesture -> + warnIfUsedOrClearWarning(Binding.GestureInput(gesture), gesturePicker) } } - // don't persist the value - return false } - @SuppressLint("CheckResult") // noAutoDismiss - private fun displayAddAxisDialog() { - val actionName = title - val axisPicker: AxisPicker = AxisPicker.inflate(context) - val dialog = - AlertDialog - .Builder(context) - .customView(view = axisPicker.rootLayout) - .title(text = actionName.toString()) - .negativeButton(R.string.dialog_cancel) { it.dismiss() } - .create() + fun showKeyPickerDialog() { + AlertDialog.Builder(context).show { + val keyPicker: KeyPicker = KeyPicker.inflate(context) + customView(view = keyPicker.rootLayout) + setTitle(title) + setIcon(icon) + + // When the user presses a key + keyPicker.setBindingChangedListener { binding -> + warnIfUsedOrClearWarning(binding, keyPicker) + } + positiveButton(R.string.dialog_ok) { + val binding = keyPicker.getBinding() ?: return@positiveButton + onKeySelected(binding) + it.dismiss() + } + negativeButton(R.string.dialog_cancel) { it.dismiss() } + keyPicker.setKeycodeValidation(KeySelectionDialogUtils.disallowModifierKeyCodes()) + } + } - axisPicker.setBindingChangedListener { binding -> - showToastIfBindingIsUsed(ReviewerBinding(binding, CardSide.BOTH)) - // Use CardSide.BOTH as placeholder just to check if binding exists - CardSideSelectionDialog.displayInstance(context) { side -> - val mappableBinding = ReviewerBinding(binding, side) - if (bindingIsUsedOnAnotherCommand(mappableBinding)) { - showDialogToReplaceBinding(mappableBinding, context.getString(R.string.binding_replace_key), dialog) - } else { - addBinding(mappableBinding) - dialog.dismiss() + fun showAddAxisDialog() { + val axisPicker = + AxisPicker.inflate(context).apply { + setBindingChangedListener { binding -> + warnIfUsedOrClearWarning(binding, warningDisplay = null) + onAxisSelected(binding) } } + AlertDialog.Builder(context).show { + customView(view = axisPicker.rootLayout) + setTitle(title) + setIcon(icon) + negativeButton(R.string.dialog_cancel) { it.dismiss() } } - - dialog.show() } - /** - * Return if another command uses - */ - private fun bindingIsUsedOnAnotherCommand(binding: MappableBinding): Boolean = getCommandWithBindingExceptThis(binding) != null - - private fun warnIfBindingIsUsed( - binding: MappableBinding, - warningDisplay: WarningDisplay, + private fun warnIfUsedOrClearWarning( + binding: Binding, + warningDisplay: WarningDisplay?, ) { - getCommandWithBindingExceptThis(binding)?.let { - val name = context.getString(it.resourceId) - val warning = context.getString(R.string.bindings_already_bound, name) - warningDisplay.setWarning(warning) - } ?: warningDisplay.clearWarning() + if (!warnIfUsed(binding, warningDisplay)) { + warningDisplay?.clearWarning() + } } +} + +class ControlPreferenceDialogFragment : DialogFragment() { + private lateinit var preference: ControlPreference - /** Displays a warning to the user if the provided binding couldn't be used */ - private fun showToastIfBindingIsUsed(binding: MappableBinding) { - val bindingCommand = - getCommandWithBindingExceptThis(binding) - ?: return + @Suppress("DEPRECATION") // targetFragment + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) - val commandName = context.getString(bindingCommand.resourceId) - val text = context.getString(R.string.bindings_already_bound, commandName) - showThemedToast(context, text, true) + val key = + requireNotNull(requireArguments().getString(SettingsFragment.PREF_DIALOG_KEY)) { + "ControlPreferenceDialogFragment must have a 'key' argument leading to its preference" + } + preference = (targetFragment as PreferenceFragmentCompat).requirePreference(key) } - /** @return command where the binding is mapped excluding the current command */ - private fun getCommandWithBindingExceptThis(binding: MappableBinding): ViewerCommand? = - MappableBinding - .allMappings(context.sharedPrefs()) - // filter to the commands which have a binding matching this one except this - .firstOrNull { x -> x.second.any { cmdBinding -> cmdBinding == binding } && x.first.preferenceKey != key } - ?.first + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val view = requireActivity().layoutInflater.inflate(R.layout.control_preference, null) + + setupAddBindingDialogs(view) + setupRemoveControlEntries(view) - private fun addBinding(binding: MappableBinding) { - val bindings = MappableBinding.fromPreferenceString(value) - // by removing the binding, we ensure it's now at the start of the list - bindings.remove(binding) - bindings.add(0, binding) - value = bindings.toPreferenceString() + return AlertDialog.Builder(requireContext()).create { + setTitle(preference.title) + setIcon(preference.icon) + customView(view, paddingTop = 24) + negativeButton(R.string.dialog_cancel) + } } - /** - * Remove binding from all control preferences other than this one - */ - private fun clearBinding(binding: MappableBinding) { - for (command in ViewerCommand.entries) { - val commandPreference = - preferenceManager.findPreference(command.preferenceKey) - ?: continue - val bindings = MappableBinding.fromPreferenceString(commandPreference.value) - if (binding in bindings) { - bindings.remove(binding) - commandPreference.value = bindings.toPreferenceString() + private fun setupAddBindingDialogs(view: View) { + view.findViewById(R.id.add_gesture).apply { + setOnClickListener { + preference.showGesturePickerDialog() + dismiss() } + isVisible = sharedPrefs().getBoolean(GestureProcessor.PREF_KEY, false) } - } - private fun showDialogToReplaceBinding( - binding: MappableBinding, - title: String, - parentDialog: DialogInterface, - ) { - val commandName = context.getString(getCommandWithBindingExceptThis(binding)!!.resourceId) + view.findViewById(R.id.add_key).setOnClickListener { + preference.showKeyPickerDialog() + dismiss() + } - AlertDialog.Builder(context).show { - title(text = title) - message(text = context.getString(R.string.bindings_already_bound, commandName)) - positiveButton(R.string.dialog_positive_replace) { - clearBinding(binding) - addBinding(binding) - parentDialog.dismiss() - } - negativeButton(R.string.dialog_cancel) + view.findViewById(R.id.add_axis).setOnClickListener { + preference.showAddAxisDialog() + dismiss() } } - companion object { - private const val ADD_AXIS_INDEX = -3 - private const val ADD_KEY_INDEX = -2 - private const val ADD_GESTURE_INDEX = -1 + private fun setupRemoveControlEntries(view: View) { + val bindings = preference.getMappableBindings().toMutableList() + if (bindings.isEmpty()) { + view.findViewById(R.id.remove_layout).isVisible = false + return + } + val titles = + bindings.map { + getString(R.string.binding_remove_binding, it.toDisplayString(requireContext())) + } + view.findViewById(R.id.list_view).apply { + adapter = ArrayAdapter(requireContext(), R.layout.control_preference_list_item, titles) + setOnItemClickListener { _, _, index, _ -> + bindings.removeAt(index) + preference.setValue(bindings.toPreferenceString()) + dismiss() + } + } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/preferences/ControlPreference2.kt b/AnkiDroid/src/main/java/com/ichi2/preferences/ControlPreference2.kt deleted file mode 100644 index 2c6ae35dec73..000000000000 --- a/AnkiDroid/src/main/java/com/ichi2/preferences/ControlPreference2.kt +++ /dev/null @@ -1,241 +0,0 @@ -/* - * Copyright (c) 2021 David Allison - * Copyright (c) 2024 Brayan Oliveira - * - * This program 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. - * - * This program 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 - * this program. If not, see . - */ -package com.ichi2.preferences - -import android.app.Dialog -import android.content.Context -import android.os.Bundle -import android.text.TextUtils -import android.util.AttributeSet -import android.view.View -import android.widget.ArrayAdapter -import android.widget.LinearLayout -import android.widget.ListView -import androidx.appcompat.app.AlertDialog -import androidx.core.view.isVisible -import androidx.fragment.app.DialogFragment -import androidx.preference.DialogPreference -import androidx.preference.PreferenceFragmentCompat -import com.ichi2.anki.R -import com.ichi2.anki.cardviewer.Gesture -import com.ichi2.anki.cardviewer.GestureProcessor -import com.ichi2.anki.dialogs.GestureSelectionDialogUtils -import com.ichi2.anki.dialogs.GestureSelectionDialogUtils.onGestureChanged -import com.ichi2.anki.dialogs.KeySelectionDialogUtils -import com.ichi2.anki.dialogs.WarningDisplay -import com.ichi2.anki.preferences.SettingsFragment -import com.ichi2.anki.preferences.requirePreference -import com.ichi2.anki.reviewer.Binding -import com.ichi2.anki.reviewer.MappableBinding -import com.ichi2.anki.reviewer.MappableBinding.Companion.toPreferenceString -import com.ichi2.anki.utils.ext.sharedPrefs -import com.ichi2.ui.AxisPicker -import com.ichi2.ui.KeyPicker -import com.ichi2.utils.create -import com.ichi2.utils.customView -import com.ichi2.utils.negativeButton -import com.ichi2.utils.positiveButton -import com.ichi2.utils.show - -/** - * A preference which allows mapping of inputs to actions (example: keys -> commands) - * - * This is implemented as a List, the elements allow the user to either add, or - * remove previously mapped keys - */ -abstract class ControlPreference2 : - DialogPreference, - DialogFragmentProvider { - @Suppress("unused") - constructor( - context: Context, - attrs: AttributeSet?, - defStyleAttr: Int, - defStyleRes: Int, - ) : super(context, attrs, defStyleAttr, defStyleRes) - - @Suppress("unused") - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - - @Suppress("unused") - constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) - - @Suppress("unused") - constructor(context: Context) : super(context) - - abstract fun getMappableBindings(): List - - abstract fun onKeySelected(binding: Binding) - - abstract fun onAxisSelected(binding: Binding) - - abstract val areGesturesEnabled: Boolean - - protected open fun onGestureSelected(gesture: Gesture) {} - - /** @return whether the binding is used in another action */ - abstract fun warnIfUsed( - binding: Binding, - warningDisplay: WarningDisplay?, - ): Boolean - - fun getValue(): String? = getPersistedString(null) - - fun setValue(value: String) { - if (!TextUtils.equals(getValue(), value)) { - persistString(value) - notifyChanged() - } - } - - override fun getSummary(): CharSequence = getMappableBindings().joinToString(", ") { it.toDisplayString(context) } - - override fun makeDialogFragment(): DialogFragment = ControlPreferenceDialogFragment() - - fun showGesturePickerDialog() { - AlertDialog.Builder(context).show { - setTitle(title) - setIcon(icon) - val gesturePicker = GestureSelectionDialogUtils.getGesturePicker(context) - positiveButton(R.string.dialog_ok) { - val gesture = gesturePicker.getGesture() ?: return@positiveButton - onGestureSelected(gesture) - it.dismiss() - } - negativeButton(R.string.dialog_cancel) { it.dismiss() } - customView(view = gesturePicker) - gesturePicker.onGestureChanged { gesture -> - warnIfUsedOrClearWarning(Binding.GestureInput(gesture), gesturePicker) - } - } - } - - fun showKeyPickerDialog() { - AlertDialog.Builder(context).show { - val keyPicker: KeyPicker = KeyPicker.inflate(context) - customView(view = keyPicker.rootLayout) - setTitle(title) - setIcon(icon) - - // When the user presses a key - keyPicker.setBindingChangedListener { binding -> - warnIfUsedOrClearWarning(binding, keyPicker) - } - positiveButton(R.string.dialog_ok) { - val binding = keyPicker.getBinding() ?: return@positiveButton - onKeySelected(binding) - it.dismiss() - } - negativeButton(R.string.dialog_cancel) { it.dismiss() } - keyPicker.setKeycodeValidation(KeySelectionDialogUtils.disallowModifierKeyCodes()) - } - } - - fun showAddAxisDialog() { - val axisPicker = - AxisPicker.inflate(context).apply { - setBindingChangedListener { binding -> - warnIfUsedOrClearWarning(binding, warningDisplay = null) - onAxisSelected(binding) - } - } - AlertDialog.Builder(context).show { - customView(view = axisPicker.rootLayout) - setTitle(title) - setIcon(icon) - negativeButton(R.string.dialog_cancel) { it.dismiss() } - } - } - - private fun warnIfUsedOrClearWarning( - binding: Binding, - warningDisplay: WarningDisplay?, - ) { - if (!warnIfUsed(binding, warningDisplay)) { - warningDisplay?.clearWarning() - } - } -} - -class ControlPreferenceDialogFragment : DialogFragment() { - private lateinit var preference: ControlPreference2 - - @Suppress("DEPRECATION") // targetFragment - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - val key = - requireNotNull(requireArguments().getString(SettingsFragment.PREF_DIALOG_KEY)) { - "ControlPreferenceDialogFragment must have a 'key' argument leading to its preference" - } - preference = (targetFragment as PreferenceFragmentCompat).requirePreference(key) - } - - override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { - val view = requireActivity().layoutInflater.inflate(R.layout.control_preference, null) - - setupAddBindingDialogs(view) - setupRemoveControlEntries(view) - - return AlertDialog.Builder(requireContext()).create { - setTitle(preference.title) - setIcon(preference.icon) - customView(view, paddingTop = 24) - negativeButton(R.string.dialog_cancel) - } - } - - private fun setupAddBindingDialogs(view: View) { - view.findViewById(R.id.add_gesture).apply { - setOnClickListener { - preference.showGesturePickerDialog() - dismiss() - } - isVisible = sharedPrefs().getBoolean(GestureProcessor.PREF_KEY, false) - } - - view.findViewById(R.id.add_key).setOnClickListener { - preference.showKeyPickerDialog() - dismiss() - } - - view.findViewById(R.id.add_axis).setOnClickListener { - preference.showAddAxisDialog() - dismiss() - } - } - - private fun setupRemoveControlEntries(view: View) { - val bindings = preference.getMappableBindings().toMutableList() - if (bindings.isEmpty()) { - view.findViewById(R.id.remove_layout).isVisible = false - return - } - val titles = - bindings.map { - getString(R.string.binding_remove_binding, it.toDisplayString(requireContext())) - } - view.findViewById(R.id.list_view).apply { - adapter = ArrayAdapter(requireContext(), R.layout.control_preference_list_item, titles) - setOnItemClickListener { _, _, index, _ -> - bindings.removeAt(index) - preference.setValue(bindings.toPreferenceString()) - dismiss() - } - } - } -} diff --git a/AnkiDroid/src/main/java/com/ichi2/preferences/ReviewerControlPreference.kt b/AnkiDroid/src/main/java/com/ichi2/preferences/ReviewerControlPreference.kt index 17f772098fa5..d600b02ec6d7 100644 --- a/AnkiDroid/src/main/java/com/ichi2/preferences/ReviewerControlPreference.kt +++ b/AnkiDroid/src/main/java/com/ichi2/preferences/ReviewerControlPreference.kt @@ -30,7 +30,7 @@ import com.ichi2.anki.reviewer.MappableBinding.Companion.toPreferenceString import com.ichi2.anki.reviewer.ReviewerBinding import com.ichi2.anki.showThemedToast -class ReviewerControlPreference : ControlPreference2 { +class ReviewerControlPreference : ControlPreference { @Suppress("unused") constructor( context: Context, diff --git a/AnkiDroid/src/main/res/layout/control_preference.xml b/AnkiDroid/src/main/res/layout/control_preference.xml index f34f202c5e3a..5ea6cd785c9f 100644 --- a/AnkiDroid/src/main/res/layout/control_preference.xml +++ b/AnkiDroid/src/main/res/layout/control_preference.xml @@ -4,7 +4,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" - tools:context="com.ichi2.preferences.ControlPreference2" + tools:context="com.ichi2.preferences.ControlPreference" >