From cc645f537efd28ee2e020edac536d53679bc8b7d Mon Sep 17 00:00:00 2001 From: Jesse Johnson Date: Tue, 2 Dec 2025 15:14:56 -0800 Subject: [PATCH] Auto-save notes when dismissing checkmark and number dialogs Previously, when a user entered notes for a habit entry, then dismissed the dialog (either intentionally or, as often the case for me, accidentally), any changes to the notes would be lost. This change seeks to avoid data loss by auto-saving notes when the dialog is closed. This handles a several data loss scenarios: - pressing the back button - clicking outside the dialog box - rotating the screen - turning off the screen (To be honest, I have done all of these at least once by accident, and some dozens of times.) As part of this change, the NumberDialog now trims whitespace from notes when manually saving to be consistent with CheckmarkDialog. --- .../common/dialogs/CheckmarkDialog.kt | 26 +++++++++++++++++-- .../activities/common/dialogs/NumberDialog.kt | 18 +++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/CheckmarkDialog.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/CheckmarkDialog.kt index 9ff4bd47..59f760f1 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/CheckmarkDialog.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/CheckmarkDialog.kt @@ -20,6 +20,7 @@ package org.isoron.uhabits.activities.common.dialogs import android.app.Dialog +import android.content.DialogInterface import android.os.Bundle import android.view.LayoutInflater import android.view.View.GONE @@ -37,11 +38,17 @@ import org.isoron.uhabits.utils.sres class CheckmarkDialog : AppCompatDialogFragment() { var onToggle: (Int, String) -> Unit = { _, _ -> } + var onDismiss: () -> Unit = {} + + private var dismissedViaSaveAction = false + private var originalNotes: String = "" + private var originalValue: Int = 0 + private lateinit var view: CheckmarkPopupBinding override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val appComponent = (requireActivity().application as HabitsApplication).component val prefs = appComponent.preferences - val view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context)) + view = CheckmarkPopupBinding.inflate(LayoutInflater.from(context)) val color = requireArguments().getInt("color") arrayOf(view.yesBtn, view.skipBtn).forEach { it.setTextColor(color) @@ -52,7 +59,9 @@ class CheckmarkDialog : AppCompatDialogFragment() { arrayOf(view.yesBtn, view.noBtn, view.skipBtn, view.unknownBtn).forEach { it.typeface = getFontAwesome(requireContext()) } - view.notes.setText(requireArguments().getString("notes")!!) + originalNotes = requireArguments().getString("notes")!! + originalValue = requireArguments().getInt("value") + view.notes.setText(originalNotes) if (!prefs.isSkipEnabled) view.skipBtn.visibility = GONE if (!prefs.areQuestionMarksEnabled) view.unknownBtn.visibility = GONE view.booleanButtons.visibility = VISIBLE @@ -62,6 +71,7 @@ class CheckmarkDialog : AppCompatDialogFragment() { setBackgroundDrawableResource(android.R.color.transparent) } fun onClick(v: Int) { + dismissedViaSaveAction = true val notes = view.notes.text.toString().trim() onToggle(v, notes) requireDialog().dismiss() @@ -77,4 +87,16 @@ class CheckmarkDialog : AppCompatDialogFragment() { return dialog } + + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + + if (!dismissedViaSaveAction) { + val currentNotes = view.notes.text.toString().trim() + if (currentNotes != originalNotes) { + onToggle(originalValue, currentNotes) + } + } + onDismiss() + } } diff --git a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberDialog.kt b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberDialog.kt index 4025bc2e..e282c3d6 100644 --- a/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberDialog.kt +++ b/uhabits-android/src/main/java/org/isoron/uhabits/activities/common/dialogs/NumberDialog.kt @@ -1,6 +1,7 @@ package org.isoron.uhabits.activities.common.dialogs import android.app.Dialog +import android.content.DialogInterface import android.os.Bundle import android.provider.Settings import android.text.method.DigitsKeyListener @@ -28,6 +29,7 @@ class NumberDialog : AppCompatDialogFragment() { var onToggle: (Double, String) -> Unit = { _, _ -> } var onDismiss: () -> Unit = {} + private var dismissedViaSaveAction = false private var originalNotes: String = "" private var originalValue: Double = 0.0 private lateinit var view: CheckmarkPopupBinding @@ -88,10 +90,21 @@ class NumberDialog : AppCompatDialogFragment() { dialog.window?.apply { setBackgroundDrawableResource(android.R.color.transparent) } - dialog.setOnDismissListener { onDismiss() } return dialog } + override fun onDismiss(dialog: DialogInterface) { + super.onDismiss(dialog) + + if (!dismissedViaSaveAction) { + val currentNotes = view.notes.text.toString().trim() + if (currentNotes != originalNotes) { + onToggle(originalValue, currentNotes) + } + } + onDismiss() + } + private fun fixDecimalSeparator(view: CheckmarkPopupBinding) { // https://stackoverflow.com/a/34256139 val separator = DecimalFormatSymbols.getInstance().decimalSeparator @@ -108,6 +121,7 @@ class NumberDialog : AppCompatDialogFragment() { } fun save() { + dismissedViaSaveAction = true var value = originalValue try { val numberFormat = NumberFormat.getInstance() @@ -120,7 +134,7 @@ class NumberDialog : AppCompatDialogFragment() { } catch (e: ParseException) { // NOP } - val notes = view.notes.text.toString() + val notes = view.notes.text.toString().trim() val location = view.saveBtn.getCenter() onToggle(value, notes) requireDialog().dismiss()