From f88f1cfb54991fcaf3c0d4593b921831fdb923c1 Mon Sep 17 00:00:00 2001 From: Quentin Hibon Date: Sun, 22 Aug 2021 19:23:13 +0200 Subject: [PATCH 1/2] Improve NumberPicker usage in numerical habits --- .../common/dialogs/NumberPickerFactory.kt | 16 +++++++++------- .../src/main/res/layout/number_picker_dialog.xml | 3 +-- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt index c531e758b..54b5700aa 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt @@ -33,6 +33,7 @@ import org.isoron.uhabits.R import org.isoron.uhabits.core.ui.screens.habits.list.ListHabitsBehavior import org.isoron.uhabits.inject.ActivityContext import org.isoron.uhabits.utils.InterfaceUtils +import java.text.DecimalFormatSymbols import javax.inject.Inject import kotlin.math.roundToLong @@ -51,7 +52,10 @@ class NumberPickerFactory val picker = view.findViewById(R.id.picker) val picker2 = view.findViewById(R.id.picker2) - val tvUnit = view.findViewById(R.id.tvUnit) + + view.findViewById(R.id.tvUnit).text = unit + view.findViewById(R.id.tvSeparator).text = + DecimalFormatSymbols.getInstance().decimalSeparator.toString() val intValue = (value * 100).roundToLong().toInt() @@ -61,19 +65,17 @@ class NumberPickerFactory picker.wrapSelectorWheel = false picker2.minValue = 0 - picker2.maxValue = 19 - picker2.setFormatter { v -> String.format("%02d", 5 * v) } - picker2.value = intValue % 100 / 5 + picker2.maxValue = 99 + picker2.setFormatter { v -> String.format("%02d", v) } + picker2.value = intValue % 100 refreshInitialValue(picker2) - tvUnit.text = unit - val dialog = AlertDialog.Builder(context) .setView(view) .setTitle(R.string.change_value) .setPositiveButton(android.R.string.ok) { _, _ -> picker.clearFocus() - val v = picker.value + 0.05 * picker2.value + val v = picker.value + 0.01 * picker2.value callback.onNumberPicked(v) } .setOnDismissListener { diff --git a/uhabits-android/src/main/res/layout/number_picker_dialog.xml b/uhabits-android/src/main/res/layout/number_picker_dialog.xml index 585a86a25..4a9ffb4ee 100644 --- a/uhabits-android/src/main/res/layout/number_picker_dialog.xml +++ b/uhabits-android/src/main/res/layout/number_picker_dialog.xml @@ -34,14 +34,13 @@ android:id="@+id/tvSeparator" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="."/> + /> Date: Sat, 4 Sep 2021 16:10:02 +0200 Subject: [PATCH 2/2] Focus fractional part after entering a decimal separator --- .../common/dialogs/NumberPickerFactory.kt | 53 ++++++++++++++++--- 1 file changed, 45 insertions(+), 8 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt index 54b5700aa..1ca119a77 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberPickerFactory.kt @@ -19,9 +19,11 @@ package org.isoron.uhabits.activities.common.dialogs +import android.annotation.SuppressLint import android.content.Context import android.content.DialogInterface import android.text.InputFilter +import android.text.Spanned import android.view.LayoutInflater import android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE import android.view.inputmethod.EditorInfo @@ -41,18 +43,24 @@ class NumberPickerFactory @Inject constructor( @ActivityContext private val context: Context ) { + fun create( value: Double, unit: String, callback: ListHabitsBehavior.NumberPickerCallback ): AlertDialog { - val inflater = LayoutInflater.from(context) val view = inflater.inflate(R.layout.number_picker_dialog, null) val picker = view.findViewById(R.id.picker) val picker2 = view.findViewById(R.id.picker2) + val watcherFilter: InputFilter = SeparatorWatcherInputFilter(picker2) + val numberPickerInputText = getNumberPickerInputText(picker) + + // watch the unfiltered input before the filters remove a possible separator from it + numberPickerInputText.filters = arrayOf(watcherFilter).plus(numberPickerInputText.filters) + view.findViewById(R.id.tvUnit).text = unit view.findViewById(R.id.tvSeparator).text = DecimalFormatSymbols.getInstance().decimalSeparator.toString() @@ -68,7 +76,6 @@ class NumberPickerFactory picker2.maxValue = 99 picker2.setFormatter { v -> String.format("%02d", v) } picker2.value = intValue % 100 - refreshInitialValue(picker2) val dialog = AlertDialog.Builder(context) .setView(view) @@ -91,20 +98,50 @@ class NumberPickerFactory InterfaceUtils.setupEditorAction( picker ) { _, actionId, _ -> - if (actionId == EditorInfo.IME_ACTION_DONE) + if (actionId == EditorInfo.IME_ACTION_DONE) { + dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick() + } + false + } + + InterfaceUtils.setupEditorAction( + picker2 + ) { _, actionId, _ -> + if (actionId == EditorInfo.IME_ACTION_DONE) { dialog.getButton(DialogInterface.BUTTON_POSITIVE).performClick() + } false } return dialog } - private fun refreshInitialValue(picker: NumberPicker) { - // Workaround for Android bug: - // https://code.google.com/p/android/issues/detail?id=35482 + @SuppressLint("DiscouragedPrivateApi") + private fun getNumberPickerInputText(picker: NumberPicker): EditText { val f = NumberPicker::class.java.getDeclaredField("mInputText") f.isAccessible = true - val inputText = f.get(picker) as EditText - inputText.filters = arrayOfNulls(0) + return f.get(picker) as EditText + } +} + +class SeparatorWatcherInputFilter(private val nextPicker: NumberPicker) : InputFilter { + override fun filter( + source: CharSequence?, + start: Int, + end: Int, + dest: Spanned?, + dstart: Int, + dend: Int + ): CharSequence { + if (source == null || source.isEmpty()) { + return "" + } + for (c in source) { + if (c == DecimalFormatSymbols.getInstance().decimalSeparator || c == '.' || c == ',') { + nextPicker.performLongClick() + break + } + } + return source } }