From 1955f3dd746948b096ebb85c718b7250a1870c60 Mon Sep 17 00:00:00 2001 From: PrathamDevX Date: Sat, 28 Feb 2026 00:24:11 +0530 Subject: [PATCH 1/3] Browser: Show error and disable OK for duplicate tags --- .../com/ichi2/anki/dialogs/tags/TagsDialog.kt | 56 ++++++++++++++++++- AnkiDroid/src/main/res/values/02-strings.xml | 1 + 2 files changed, 54 insertions(+), 3 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt index 1891cdf07e15..f75b8592637c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt @@ -22,6 +22,7 @@ import androidx.core.content.ContextCompat import androidx.core.os.BundleCompat import androidx.core.os.bundleOf import androidx.core.view.isVisible +import androidx.core.widget.doAfterTextChanged import androidx.fragment.app.viewModels import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope @@ -386,17 +387,66 @@ class TagsDialog : AnalyticsDialogFragment { inputType = InputType.TYPE_CLASS_TEXT, displayKeyboard = true, ) { d: AlertDialog?, input: CharSequence -> + addTag(input.toString()) d?.dismiss() } + val inputET = addTagDialog.getInputField() + inputET.filters = arrayOf(addTagFilter) + if (!prefixTag.isNullOrEmpty()) { - // utilize the addTagFilter to append '::' properly by appending a space to prefixTag inputET.setText("$prefixTag ") + inputET.moveCursorToEnd() + } + + val positiveButton = + addTagDialog.getButton(AlertDialog.BUTTON_POSITIVE) + + positiveButton.isEnabled = false + + // SAFE WAY TO FIND TextInputLayout + val textInputLayout = + generateSequence(inputET.parent) { (it as? View)?.parent } + .filterIsInstance() + .firstOrNull() + + inputET.doAfterTextChanged { text -> + + val rawTag = text?.toString()?.trim() + + if (rawTag.isNullOrEmpty()) { + textInputLayout?.error = null + positiveButton.isEnabled = false + + return@doAfterTextChanged + } + + lifecycleScope.launch { + val tags = viewModel.tags.await() + + val normalized = + TagsUtil.getUniformedTag(rawTag) + + val exists = + tags.contains(normalized) + + if (exists) { + textInputLayout?.error = + getString( + R.string.tag_already_exists, + normalized, + ) + + positiveButton.isEnabled = false + } else { + textInputLayout?.error = null + + positiveButton.isEnabled = true + } + } } - inputET.moveCursorToEnd() - addTagDialog.show() } @VisibleForTesting diff --git a/AnkiDroid/src/main/res/values/02-strings.xml b/AnkiDroid/src/main/res/values/02-strings.xml index 8c8882821128..fbd662cb0634 100644 --- a/AnkiDroid/src/main/res/values/02-strings.xml +++ b/AnkiDroid/src/main/res/values/02-strings.xml @@ -190,6 +190,7 @@ This is a special deck for studying outside of the normal schedule. Cards will be automatically returned to their original decks after you review them. Deleting this deck from the deck list will return all remaining cards to their original deck. Touch “%2$s” to confirm adding “%1$s” Existing tag “%1$s” selected + Tag "%1$s" already exists From 255d980038946ab1c761a8ab2cbcde517d2811e2 Mon Sep 17 00:00:00 2001 From: PrathamDevX Date: Sun, 1 Mar 2026 11:58:42 +0530 Subject: [PATCH 2/3] Browser: show error and disable OK for duplicate tags --- .../com/ichi2/anki/dialogs/tags/TagsDialog.kt | 18 +++++++----------- AnkiDroid/src/main/res/values/02-strings.xml | 2 +- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt index f75b8592637c..f1116393097c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt @@ -387,7 +387,6 @@ class TagsDialog : AnalyticsDialogFragment { inputType = InputType.TYPE_CLASS_TEXT, displayKeyboard = true, ) { d: AlertDialog?, input: CharSequence -> - addTag(input.toString()) d?.dismiss() } @@ -397,20 +396,20 @@ class TagsDialog : AnalyticsDialogFragment { inputET.filters = arrayOf(addTagFilter) if (!prefixTag.isNullOrEmpty()) { + // utilize the addTagFilter to append '::' properly by appending a space to prefixTag inputET.setText("$prefixTag ") - inputET.moveCursorToEnd() } + inputET.moveCursorToEnd() + val positiveButton = addTagDialog.getButton(AlertDialog.BUTTON_POSITIVE) positiveButton.isEnabled = false - // SAFE WAY TO FIND TextInputLayout val textInputLayout = - generateSequence(inputET.parent) { (it as? View)?.parent } - .filterIsInstance() - .firstOrNull() + inputET.parent?.parent + as? com.google.android.material.textfield.TextInputLayout inputET.doAfterTextChanged { text -> @@ -419,7 +418,6 @@ class TagsDialog : AnalyticsDialogFragment { if (rawTag.isNullOrEmpty()) { textInputLayout?.error = null positiveButton.isEnabled = false - return@doAfterTextChanged } @@ -434,10 +432,7 @@ class TagsDialog : AnalyticsDialogFragment { if (exists) { textInputLayout?.error = - getString( - R.string.tag_already_exists, - normalized, - ) + getString(R.string.tag_already_exists) positiveButton.isEnabled = false } else { @@ -447,6 +442,7 @@ class TagsDialog : AnalyticsDialogFragment { } } } + addTagDialog.show() } @VisibleForTesting diff --git a/AnkiDroid/src/main/res/values/02-strings.xml b/AnkiDroid/src/main/res/values/02-strings.xml index fbd662cb0634..5f6a13d1506f 100644 --- a/AnkiDroid/src/main/res/values/02-strings.xml +++ b/AnkiDroid/src/main/res/values/02-strings.xml @@ -190,7 +190,7 @@ This is a special deck for studying outside of the normal schedule. Cards will be automatically returned to their original decks after you review them. Deleting this deck from the deck list will return all remaining cards to their original deck. Touch “%2$s” to confirm adding “%1$s” Existing tag “%1$s” selected - Tag "%1$s" already exists + Tag already exists From ecf0609376a3fe01670f8cc82679be3c82747742 Mon Sep 17 00:00:00 2001 From: PrathamDevX Date: Sun, 8 Mar 2026 11:52:02 +0530 Subject: [PATCH 3/3] fix: remove unwanted formatting changes --- .../src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt | 5 ----- 1 file changed, 5 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt index f1116393097c..3fcc18d20aac 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/tags/TagsDialog.kt @@ -390,18 +390,13 @@ class TagsDialog : AnalyticsDialogFragment { addTag(input.toString()) d?.dismiss() } - val inputET = addTagDialog.getInputField() - inputET.filters = arrayOf(addTagFilter) - if (!prefixTag.isNullOrEmpty()) { // utilize the addTagFilter to append '::' properly by appending a space to prefixTag inputET.setText("$prefixTag ") } - inputET.moveCursorToEnd() - val positiveButton = addTagDialog.getButton(AlertDialog.BUTTON_POSITIVE)