ankidroid / Anki-Android

AnkiDroid: Anki flashcards on Android. Your secret trick to achieve superhuman information retention.
GNU General Public License v3.0
8.59k stars 2.23k forks source link

Restore Deck Description functionality (add/view) #15312

Closed david-allison closed 8 months ago

david-allison commented 9 months ago

Do we have an issue for 'description'? I feel that's a blocker for 2.17

Originally posted by @david-allison in https://github.com/ankidroid/Anki-Android/issues/14955#issuecomment-1904710284

BrayanDSO commented 9 months ago

For reference, this is the screen that shows the description in Anki. Also that is the dialog when Description is pressed

image

IIRC, MaterialTextView can handle HTML, so it shouldn't be much of a problem

Our equivalent screen looks like this one, so I believe that the description should be put there

image

david-allison commented 9 months ago

MaterialTextView can handle HTML

When we previously had this functionality, it was a subset (Editable) using a normal TextView, does MaterialTextView offer improvements here?

BrayanDSO commented 9 months ago

Nah, it's the same thing but uses some different default layout/attributes. I said it as an example to keep things consistent among the code base

david-allison commented 9 months ago

Relevant:

https://github.com/ankidroid/Anki-Android/blob/672b075a546e806e93f8e2c158749231c065a021/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt#L581-L592

jaivsh commented 8 months ago

Hi @david-allison, I want to work on this issue of developing the description panel in the given screen. Please assign this issue to me! Thank you

david-allison commented 8 months ago

Hi @jaivsh, this issue is close to blocking the release of 2.17.

Normally we wouldn't impose deadlines, but this issue is more likely to be reassigned. If you'd rather work on a different issue, do let me know

The first job would be to get consensus on where to access the input screen.

I feel that adding an "edit" icon near the current description would be best from a UX perspective

jaivsh commented 8 months ago

@david-allison thanks for assigning it to me! I'd try my best to do this asap.

criticalAY commented 8 months ago

@jaivsh Made any progress?

david-allison commented 8 months ago

Had a hack at this. Patch attached. Everything is done aside from:

Screenshot 2024-02-14 at 06 39 46
```patch Subject: [PATCH] refactor: extract Deck.description * remove `Deck.getActualDescription()` --- Index: AnkiDroid/src/main/res/values/01-core.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/AnkiDroid/src/main/res/values/01-core.xml b/AnkiDroid/src/main/res/values/01-core.xml --- a/AnkiDroid/src/main/res/values/01-core.xml (revision e89cffbbadecd820a3944c88ee8bba932bb0ced1) +++ b/AnkiDroid/src/main/res/values/01-core.xml (date 1707887756994) @@ -71,6 +71,7 @@ Rename deck Create shortcut Browse cards + Edit description Add Add note Sync account Index: AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/Deck.kt IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/Deck.kt b/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/Deck.kt --- a/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/Deck.kt (revision e89cffbbadecd820a3944c88ee8bba932bb0ced1) +++ b/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/Deck.kt (date 1707888043925) @@ -17,7 +17,15 @@ package com.ichi2.anki.utils.ext import com.ichi2.libanki.Deck +import com.ichi2.libanki.DeckId +import com.ichi2.libanki.Decks var Deck.description: String get() = optString("desc", "") set(value) { put("desc", value) } + +fun Decks.update(did: DeckId, block: Deck.() -> Unit) { + val deck = get(did)!! + block(deck) + this.save(deck) +} \ No newline at end of file Index: AnkiDroid/src/main/res/values/02-strings.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/AnkiDroid/src/main/res/values/02-strings.xml b/AnkiDroid/src/main/res/values/02-strings.xml --- a/AnkiDroid/src/main/res/values/02-strings.xml (revision e89cffbbadecd820a3944c88ee8bba932bb0ced1) +++ b/AnkiDroid/src/main/res/values/02-strings.xml (date 1707888343127) @@ -423,4 +423,6 @@ Please log in to download more decks + + Description Index: AnkiDroid/src/main/res/layout/dialog_deck_description.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/AnkiDroid/src/main/res/layout/dialog_deck_description.xml b/AnkiDroid/src/main/res/layout/dialog_deck_description.xml new file mode 100644 --- /dev/null (date 1707892329461) +++ b/AnkiDroid/src/main/res/layout/dialog_deck_description.xml (date 1707892329461) @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + Index: AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerContextMenu.kt IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerContextMenu.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerContextMenu.kt --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerContextMenu.kt (revision e89cffbbadecd820a3944c88ee8bba932bb0ced1) +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DeckPickerContextMenu.kt (date 1707888991434) @@ -73,6 +73,9 @@ add(DeckPickerContextMenuOption.UNBURY) } add(DeckPickerContextMenuOption.CREATE_SHORTCUT) + if (!dyn) { + add(DeckPickerContextMenuOption.EDIT_DESCRIPTION) + } add(DeckPickerContextMenuOption.DELETE_DECK) } @@ -88,6 +91,7 @@ CREATE_SUBDECK(R.string.create_subdeck), CREATE_SHORTCUT(R.string.create_shortcut), BROWSE_CARDS(R.string.browse_cards), + EDIT_DESCRIPTION(R.string.edit_deck_description), ADD_CARD(R.string.menu_add); } Index: AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EditDeckDescriptionDialog.kt IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EditDeckDescriptionDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EditDeckDescriptionDialog.kt new file mode 100644 --- /dev/null (date 1707892677863) +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/EditDeckDescriptionDialog.kt (date 1707892677863) @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2024 David Allison + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +package com.ichi2.anki.dialogs + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.os.bundleOf +import androidx.fragment.app.DialogFragment +import com.google.android.material.appbar.MaterialToolbar +import com.google.android.material.textfield.TextInputEditText +import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.anki.R +import com.ichi2.anki.launchCatchingTask +import com.ichi2.anki.utils.ext.description +import com.ichi2.anki.utils.ext.update +import com.ichi2.libanki.DeckId +import com.ichi2.themes.Themes +import timber.log.Timber + + +class EditDeckDescriptionDialog : DialogFragment() { + private val deckId: DeckId + get() = requireArguments().getLong(ARG_DECK_ID) + + private lateinit var deckDescriptionInput: TextInputEditText + + private var currentDescription + get() = deckDescriptionInput.text.toString() + set(value) { deckDescriptionInput.setText(value) } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + Themes.setTheme(requireContext()) + return inflater.inflate(R.layout.dialog_deck_description, null).apply { + deckDescriptionInput = this.findViewById(R.id.deck_description_input) + launchCatchingTask { + currentDescription = getDescription() + } + findViewById(R.id.topAppBar).apply { + setNavigationOnClickListener { + onBack() + } + + setOnMenuItemClickListener { menuItem -> + if (menuItem.itemId == R.id.action_save) { + saveAndExit() + true + } + else false + } + }.also { toolbar -> + launchCatchingTask { toolbar.title = withCol { decks.get(deckId)!!.name } } + } + } + } + + override fun onStart() { + super.onStart() + + dialog!!.window!!.setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT + ) + } + + private fun saveAndExit() = launchCatchingTask { + setDescription(currentDescription) + dismiss() + } + + private fun onBack() = launchCatchingTask { + fun closeWithoutSaving() { + Timber.i("Closing dialog without saving") + closeWithoutSaving() + } + + if (getDescription() == currentDescription) { + closeWithoutSaving() + return@launchCatchingTask + } + + Timber.i("asking if user should discard changes") + DiscardChangesDialog.showDialog(requireContext()) { + closeWithoutSaving() + } + } + + private suspend fun getDescription() = withCol { decks.get(deckId)!!.description } + + private suspend fun setDescription(value: String) { + Timber.d("updating description") + withCol { decks.update(deckId) { description = value } } + } + + companion object { + private const val ARG_DECK_ID = "deckId" + + fun newInstance(deckId: DeckId): EditDeckDescriptionDialog { + return EditDeckDescriptionDialog().apply { + arguments = bundleOf( + ARG_DECK_ID to deckId + ) + } + } + } +} + + Index: AnkiDroid/src/main/res/menu/menu_deck_description.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/AnkiDroid/src/main/res/menu/menu_deck_description.xml b/AnkiDroid/src/main/res/menu/menu_deck_description.xml new file mode 100644 --- /dev/null (date 1707889916236) +++ b/AnkiDroid/src/main/res/menu/menu_deck_description.xml (date 1707889916236) @@ -0,0 +1,26 @@ + + + + + + + \ No newline at end of file Index: AnkiDroid/src/main/res/drawable/close_icon.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/AnkiDroid/src/main/res/drawable/close_icon.xml b/AnkiDroid/src/main/res/drawable/close_icon.xml --- a/AnkiDroid/src/main/res/drawable/close_icon.xml (revision e89cffbbadecd820a3944c88ee8bba932bb0ced1) +++ b/AnkiDroid/src/main/res/drawable/close_icon.xml (date 1707888742564) @@ -1,4 +1,4 @@ - Index: AnkiDroid/src/main/java/com/ichi2/libanki/Decks.kt IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/Decks.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/Decks.kt --- a/AnkiDroid/src/main/java/com/ichi2/libanki/Decks.kt (revision e89cffbbadecd820a3944c88ee8bba932bb0ced1) +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/Decks.kt (date 1707887756994) @@ -251,8 +251,6 @@ * The methods below are not in LibAnki. * *********************************************************** */ - @KotlinCleanup("convert to extension method (possibly in servicelayer)") - fun getActualDescription(): String = current().optString("desc", "") /** @return the fully qualified name of the subdeck, or null if unavailable */ fun getSubdeckName(did: DeckId, subdeckName: String?): String? { Index: AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt --- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt (revision e89cffbbadecd820a3944c88ee8bba932bb0ced1) +++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt (date 1707890114742) @@ -591,6 +591,10 @@ addNote() dismissAllDialogFragments() } + DeckPickerContextMenuOption.EDIT_DESCRIPTION -> { + dismissAllDialogFragments() + showDialogFragment(EditDeckDescriptionDialog.newInstance(deckId)) + } } } ```