From ef9dbc469772ab37c4044b233d3fdd61977bfadb Mon Sep 17 00:00:00 2001 From: ShaanNarendran Date: Sun, 18 Jan 2026 01:55:14 +0530 Subject: [PATCH] Fix: App crashing on performing multiple clicks on add button in note editor Fixes 17375 Co-authored-by: David Allison <62114487+david-allison@users.noreply.github.com> --- .../java/com/ichi2/anki/NoteEditorFragment.kt | 6 ++++-- .../java/com/ichi2/anki/utils/OnlyOnce.kt | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt index 041f328421d4..8e5c789113da 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt @@ -153,6 +153,7 @@ import com.ichi2.anki.snackbar.BaseSnackbarBuilderProvider import com.ichi2.anki.snackbar.SnackbarBuilder import com.ichi2.anki.snackbar.showSnackbar import com.ichi2.anki.ui.setupNoteTypeSpinner +import com.ichi2.anki.utils.RunOnlyOnce import com.ichi2.anki.utils.ext.sharedPrefs import com.ichi2.anki.utils.ext.showDialogFragment import com.ichi2.anki.utils.ext.window @@ -224,7 +225,7 @@ class NoteEditorFragment : private var changed = false private var isTagsEdited = false private var isFieldEdited = false - + private var addNoteJob = RunOnlyOnce(scope = lifecycleScope) private var multimediaActionJob: Job? = null private val getColUnsafe: Collection @@ -1338,7 +1339,8 @@ class NoteEditorFragment : reloadRequired = true - lifecycleScope.launch { + // Use addNoteJob to ignore any requests made after the initial add note request, this prevents multiple clicks from crashing the app + addNoteJob.launch { val noteFieldsCheck = checkNoteFieldsResponse(editorNote!!) if (noteFieldsCheck is NoteFieldsCheckResult.Failure) { addNoteErrorMessage = noteFieldsCheck.localizedMessage ?: getString(R.string.something_wrong) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/utils/OnlyOnce.kt b/AnkiDroid/src/main/java/com/ichi2/anki/utils/OnlyOnce.kt index 94c02273432c..8856ab60fffa 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/utils/OnlyOnce.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/utils/OnlyOnce.kt @@ -16,7 +16,9 @@ package com.ichi2.anki.utils +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job +import kotlinx.coroutines.launch import timber.log.Timber /** @@ -51,3 +53,22 @@ object OnlyOnce { } } } + +/** + * Enforces single execution of a coroutine task + * if a task is running, ignore any other requests till it finishes + * @param scope The coroutine scope where the task is launched + */ +class RunOnlyOnce( + private val scope: CoroutineScope, +) { + private var job: Job? = null + + fun launch(block: suspend CoroutineScope.() -> Unit) { + if (job?.isActive == true) { + Timber.d("skipped multiple executions of job") + return + } + job = scope.launch(block = block) + } +}