diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt index 7e975e4668b7..5882fd5d52c1 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt @@ -384,7 +384,7 @@ open class DeckPicker : Triple( decks.name(deckId), decks.isFiltered(deckId), - sched.haveBuriedInCurrentDeck() + sched.haveBuried() ) } updateDeckList() // focus has changed diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt index 1598b3ded7bc..0420a859afee 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt @@ -344,7 +344,7 @@ class StudyOptionsFragment : Fragment(), ChangeManager.Subscriber, Toolbar.OnMen menu.findItem(R.id.action_export).isVisible = false } // Switch on or off unbury depending on if there are cards to unbury - menu.findItem(R.id.action_unbury).isVisible = col != null && col!!.sched.haveBuriedInCurrentDeck() + menu.findItem(R.id.action_unbury).isVisible = col != null && col!!.sched.haveBuried() // Set the proper click target for the undo button's ActionProvider val undoActionProvider: RtlCompliantActionProvider? = MenuItemCompat.getActionProvider( menu.findItem(R.id.action_undo) diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/Decks.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/Decks.kt index efa9a98b5a08..b2f41e398b76 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/Decks.kt +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/Decks.kt @@ -25,6 +25,7 @@ package com.ichi2.libanki +import androidx.annotation.CheckResult import anki.collection.OpChanges import anki.collection.OpChangesWithCount import anki.collection.OpChangesWithId @@ -34,6 +35,7 @@ import anki.decks.DeckTreeNode import anki.decks.FilteredDeckForUpdate import anki.decks.SetDeckCollapsedRequest import com.google.protobuf.kotlin.toByteStringUtf8 +import com.ichi2.annotations.NeedsTest import com.ichi2.libanki.backend.BackendUtils import com.ichi2.libanki.utils.* import com.ichi2.utils.jsonObjectIterable @@ -417,11 +419,10 @@ class Decks(private val col: Collection) { ************************************************************* */ - @RustCleanup("implement and make public") + @CheckResult @LibAnkiAlias("set_current") - @Suppress("unused", "unused_parameter") - private fun setCurrent(deck: DeckId): OpChanges { - TODO() + fun setCurrent(deck: DeckId): OpChanges { + return col.backend.setCurrentDeck(deck) } /** @return The currently selected deck ID. */ @@ -461,7 +462,81 @@ class Decks(private val col: Collection) { ************************************************************* */ - // TODO + /** The deck of did and all its children, as (name, id). */ + @LibAnkiAlias("deck_and_child_name_ids") + fun deckAndChildNameIds(deckId: DeckId): List> { + return col.backend.getDeckAndChildNames(deckId).map { entry -> + entry.name to entry.id + } + } + + /** All children of did, as (name, id). */ + @LibAnkiAlias("children") + fun children(did: DeckId): List> { + return deckAndChildNameIds(did).filter { + it.second != did + } + } + + @LibAnkiAlias("child_ids") + fun childIds(parentName: String): List { + val parentId = idForName(parentName) ?: return emptyList() + return children(parentId).map { it.second } + } + + @LibAnkiAlias("deck_and_child_ids") + fun deckAndChildIds(deckId: DeckId): List { + return col.backend.getDeckAndChildNames(deckId).map { entry -> + entry.id + } + } + + /** All parents of did. */ + @LibAnkiAlias("parents") + fun parents(did: DeckId, nameMap: Map? = null): List { + // get parent and grandparent names + val parentsNames = mutableListOf() + for (part in immediateParentPath(get(did)!!.name)) { + if (parentsNames.isEmpty()) { + parentsNames.add(part) + } else { + parentsNames.append("${parentsNames.last()}::$part") + } + } + // convert to objects + val parents = mutableListOf() + for (parentName in parentsNames) { + val deck = if (nameMap != null) { + nameMap[parentName] + } else { + get(id(parentName)) + }!! + parents.add(deck) + } + return parents.toList() + } + + /** All existing parents of [name] */ + @NeedsTest("implementation matches Anki's") + @LibAnkiAlias("parents_by_name") + fun parentsByName(name: String): List { + if (!name.contains("::")) { + return listOf() + } + val names = immediateParentPath(name) + val head = mutableListOf() + val parents = mutableListOf() + + for (deckName in names) { + head.add(deckName) + val deck = byName(head.joinToString("::")) + if (deck != null) { + parents.append(deck) + } + } + + return parents.toList() + } /* * Filtered decks @@ -513,6 +588,20 @@ class Decks(private val col: Collection) { return path(name).last() } + @LibAnkiAlias("immediate_parent_path") + fun immediateParentPath(name: String): List { + return path(name).dropLast(1) + } + + @LibAnkiAlias("immediate_parent") + fun immediateParent(name: String): String? { + val parentPath = immediateParentPath(name) + if (parentPath.isNotEmpty()) { + return parentPath.joinToString("::") + } + return null + } + /** Invalid id, represents an id on an unfound deck */ const val NOT_FOUND_DECK_ID = -1L diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Scheduler.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Scheduler.kt index c37fbb58de77..4817be43a10b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Scheduler.kt +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Scheduler.kt @@ -17,6 +17,7 @@ package com.ichi2.libanki.sched import android.app.Activity +import androidx.annotation.CheckResult import androidx.annotation.VisibleForTesting import androidx.annotation.WorkerThread import anki.collection.OpChanges @@ -289,10 +290,9 @@ open class Scheduler(val col: Collection) { /** * @return Whether there are buried card is selected deck */ - open fun haveBuriedInCurrentDeck(): Boolean { - return col.backend.congratsInfo().run { - haveUserBuried || haveSchedBuried - } + fun haveBuried(): Boolean { + val info = congratulationsInfo() + return info.haveUserBuried || info.haveSchedBuried } /** @return whether there are cards in learning, with review due the same @@ -434,6 +434,27 @@ open class Scheduler(val col: Collection) { return Utils.ids2str(col.decks.active()) } + fun congratulationsInfo(): CongratsInfoResponse { + return col.backend.congratsInfo() + } + + fun haveManuallyBuried(): Boolean { + return congratulationsInfo().haveUserBuried + } + + fun haveBuriedSiblings(): Boolean { + return congratulationsInfo().haveSchedBuried + } + + @CheckResult + fun customStudy(request: CustomStudyRequest): OpChanges { + return col.backend.customStudy(request) + } + + fun customStudyDefaults(deckId: DeckId): CustomStudyDefaultsResponse { + return col.backend.customStudyDefaults(deckId) + } + /** * @return Number of new card in current deck and its descendants. Capped at [REPORT_LIMIT] */ diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/DeckPickerTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/DeckPickerTest.kt index 0b570d8e3e1a..b806f89268d2 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/DeckPickerTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/DeckPickerTest.kt @@ -478,9 +478,9 @@ class DeckPickerTest : RobolectricTest() { getColUnsafe.sched.buryCards(listOf(card.id)) updateDeckList() assertEquals(1, visibleDeckCount) - assertTrue(getColUnsafe.sched.haveBuriedInCurrentDeck(), "Deck should have buried cards") + assertTrue(getColUnsafe.sched.haveBuried(), "Deck should have buried cards") supportFragmentManager.selectContextMenuOption(DeckPickerContextMenuOption.UNBURY, deckId) - kotlin.test.assertFalse(getColUnsafe.sched.haveBuriedInCurrentDeck()) + kotlin.test.assertFalse(getColUnsafe.sched.haveBuried()) } } @@ -674,7 +674,7 @@ class DeckPickerTest : RobolectricTest() { // select a deck with no cards col.decks.select(emptyDeck) - assertThat("unbury is not visible: deck has no cards", !col.sched.haveBuriedInCurrentDeck()) + assertThat("unbury is not visible: deck has no cards", !col.sched.haveBuried()) deckPicker { assertThat("deck focus is set", focusedDeck, equalTo(emptyDeck)) @@ -686,7 +686,7 @@ class DeckPickerTest : RobolectricTest() { deckToClick.performLongClick() // ASSERT - assertThat("unbury is visible: one card is buried", col.sched.haveBuriedInCurrentDeck()) + assertThat("unbury is visible: one card is buried", col.sched.haveBuried()) assertThat("deck focus has changed", focusedDeck, equalTo(deckWithCards)) } }