From 6dcad294c7d5d72855530d77da9d0b0343781466 Mon Sep 17 00:00:00 2001 From: Robozinho <65715921+RobozinhoD@users.noreply.github.com> Date: Sat, 5 Oct 2024 10:49:10 -0300 Subject: [PATCH] Add restore to default feature --- .../java/com/ichi2/anki/CardTemplateEditor.kt | 66 +++++++++++++++++++ .../main/java/com/ichi2/libanki/Notetypes.kt | 30 +++++++++ .../main/res/menu/card_template_editor.xml | 8 ++- 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt index 9730bbcb3751..f1276b5b5d95 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CardTemplateEditor.kt @@ -18,6 +18,7 @@ package com.ichi2.anki import android.content.Context +import android.content.DialogInterface import android.content.Intent import android.os.Bundle import android.os.Handler @@ -49,10 +50,14 @@ import androidx.fragment.app.commitNow import androidx.lifecycle.Lifecycle import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 +import anki.notetypes.StockNotetype +import anki.notetypes.StockNotetype.OriginalStockKind.ORIGINAL_STOCK_KIND_UNKNOWN_VALUE +import anki.notetypes.notetypeId import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.snackbar.Snackbar import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayoutMediator +import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol import com.ichi2.anki.android.input.ShortcutGroup import com.ichi2.anki.android.input.shortcut @@ -83,12 +88,18 @@ import com.ichi2.libanki.NotetypeJson import com.ichi2.libanki.Notetypes import com.ichi2.libanki.Notetypes.Companion.NOT_FOUND_NOTE_TYPE import com.ichi2.libanki.exception.ConfirmModSchemaException +import com.ichi2.libanki.getStockNotetype +import com.ichi2.libanki.getStockNotetypeKinds +import com.ichi2.libanki.restoreNotetypeToStock +import com.ichi2.libanki.undoableOp import com.ichi2.themes.Themes import com.ichi2.ui.FixedEditText import com.ichi2.ui.FixedTextView import com.ichi2.utils.KotlinCleanup import com.ichi2.utils.copyToClipboard import com.ichi2.utils.jsonObjectIterable +import com.ichi2.utils.listItems +import com.ichi2.utils.show import net.ankiweb.rsdroid.Translations import org.json.JSONArray import org.json.JSONException @@ -842,6 +853,7 @@ open class CardTemplateEditor : * Setups the part of the menu that can be used either in template editor or in previewer fragment. */ fun setupCommonMenu(menu: Menu) { + menu.findItem(R.id.action_restore_to_default).title = CollectionManager.TR.cardTemplatesRestoreToDefault() if (noteTypeCreatesDynamicNumberOfNotes()) { Timber.d("Editing cloze/occlusion model, disabling add/delete card template and deck override functionality") menu.findItem(R.id.action_add).isVisible = false @@ -874,11 +886,24 @@ open class CardTemplateEditor : menu.findItem(R.id.action_insert_field).isVisible = isInsertFieldItemVisible } + @NeedsTest("Notetype is restored to stock kind") + private suspend fun restoreNotetypeToStock(kind: StockNotetype.Kind? = null) { + val nid = notetypeId { ntid = tempModel.modelId } + undoableOp { restoreNotetypeToStock(nid, kind) } + onModelSaved() + showThemedToast( + requireContext(), + TR.cardTemplatesRestoredToDefault(), + shortLength = false, + ) + } + /** * Handles the part of the menu set by [setupCommonMenu]. * @returns whether the given item was handled * @see [onMenuItemSelected] and [onMenuItemClick] */ + @NeedsTest("Restore to default option") fun handleCommonMenuItemSelected(menuItem: MenuItem): Boolean { return when (menuItem.itemId) { R.id.action_add -> { @@ -928,6 +953,47 @@ open class CardTemplateEditor : Timber.i("CardTemplateEditor::Card Browser Template button pressed") openBrowserAppearance() } + R.id.action_restore_to_default -> { + Timber.i("CardTemplateEditor:: Restore to default button pressed") + + fun askUser(kind: StockNotetype.Kind? = null) { + AlertDialog.Builder(requireContext()).show { + setTitle(TR.cardTemplatesRestoreToDefault()) + setMessage(TR.cardTemplatesRestoreToDefaultConfirmation()) + setPositiveButton(R.string.dialog_ok) { _, _ -> + launchCatchingTask { + restoreNotetypeToStock(kind) + } + } + setNegativeButton(R.string.dialog_cancel) { _, _ -> } + } + } + + val originalStockKind = tempModel.notetype.optInt("originalStockKind", ORIGINAL_STOCK_KIND_UNKNOWN_VALUE) + if (originalStockKind != ORIGINAL_STOCK_KIND_UNKNOWN_VALUE) { + Timber.d("Asking to restore to original stock kind %s", originalStockKind) + askUser() + return true + } + + launchCatchingTask { + Timber.d("Unknown stock kind: asking which kind to restore to") + val stockNotetypeKinds = getStockNotetypeKinds() + val stockNotetypesNames = + withCol { + stockNotetypeKinds.map { getStockNotetype(it).name } + } + AlertDialog.Builder(requireContext()).show { + setTitle(TR.cardTemplatesRestoreToDefault()) + setNegativeButton(R.string.dialog_cancel) { _, _ -> } + listItems(stockNotetypesNames) { _: DialogInterface, index: Int -> + val kind = stockNotetypeKinds[index] + askUser(kind) + } + } + } + true + } else -> { return false } diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/Notetypes.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/Notetypes.kt index cf7ce87d5dcc..8e74a8a270e5 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/Notetypes.kt +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/Notetypes.kt @@ -31,18 +31,22 @@ package com.ichi2.libanki +import androidx.annotation.CheckResult import anki.collection.OpChanges import anki.collection.OpChangesWithId import anki.notetypes.Notetype +import anki.notetypes.NotetypeId import anki.notetypes.NotetypeNameId import anki.notetypes.NotetypeNameIdUseCount import anki.notetypes.StockNotetype +import anki.notetypes.restoreNotetypeToStockRequest import com.google.protobuf.ByteString import com.ichi2.anki.CrashReportService import com.ichi2.annotations.NeedsTest import com.ichi2.libanki.Consts.MODEL_CLOZE import com.ichi2.libanki.Utils.checksum import com.ichi2.libanki.backend.BackendUtils +import com.ichi2.libanki.backend.BackendUtils.fromJsonBytes import com.ichi2.libanki.backend.BackendUtils.toJsonBytes import com.ichi2.libanki.exception.ConfirmModSchemaException import com.ichi2.libanki.utils.LibAnkiAlias @@ -776,3 +780,29 @@ fun Collection.getNotetypeNames(): List = backend.getNotetypeNam fun Collection.addNotetypeLegacy(json: ByteString): OpChangesWithId = backend.addNotetypeLegacy(json = json) fun Collection.getStockNotetypeLegacy(kind: StockNotetype.Kind): ByteString = backend.getStockNotetypeLegacy(kind = kind) + +fun Collection.getStockNotetype(kind: StockNotetype.Kind): NotetypeJson = NotetypeJson(fromJsonBytes(getStockNotetypeLegacy(kind))) + +/** + * Restores a notetype to its original stock kind. + * + * @param notetypeId id of the changed notetype + * @param forceKind optional stock kind to be forced instead of the original kind. + * Older notetypes did not store their original stock kind, so we allow the UI + * to pass in an override to use when missing, or for tests. + */ +@CheckResult +fun Collection.restoreNotetypeToStock( + notetypeId: NotetypeId, + forceKind: StockNotetype.Kind? = null, +): OpChanges { + val msg = + restoreNotetypeToStockRequest { + this.notetypeId = notetypeId + forceKind?.let { this.forceKind = forceKind } + } + return backend.restoreNotetypeToStock(msg) +} + +@NotInLibAnki +fun getStockNotetypeKinds(): List = StockNotetype.Kind.entries.filter { it != StockNotetype.Kind.UNRECOGNIZED } diff --git a/AnkiDroid/src/main/res/menu/card_template_editor.xml b/AnkiDroid/src/main/res/menu/card_template_editor.xml index 86515e849516..161d4ce25eba 100644 --- a/AnkiDroid/src/main/res/menu/card_template_editor.xml +++ b/AnkiDroid/src/main/res/menu/card_template_editor.xml @@ -1,5 +1,6 @@ + xmlns:ankidroid="http://schemas.android.com/apk/res-auto" + xmlns:tools="http://schemas.android.com/tools"> +