From 27689811a25e34201d62e7d09179a53480f40374 Mon Sep 17 00:00:00 2001 From: Onur Dursun Date: Wed, 13 May 2026 20:37:47 +0300 Subject: [PATCH] fix: deck picker subtitle ignores 9999 cap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The subtitle reads the deck tree protobuf root's new, review and learn count fields, each independently clamped at 9999 by the backend. As a result the collection wide total stops at 9999 (or 3 × 9999 in mixed queue collections) even when the user has many more cards due. The per deck 9999 clamp is intentional in the upstream backend, but the overall total at the top of the deck picker is not. Route that total through Scheduler.counts (which sources from getQueuedCards and is both limit aware and unclamped, same as StudyOptionsFragment) summed across top level decks. Side benefit: Custom Study limit extensions are now reflected in the subtitle. Fixes #17605 --- .../anki/deckpicker/DeckPickerViewModel.kt | 7 ++++- .../com/ichi2/anki/utils/ext/Scheduler.kt | 27 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/deckpicker/DeckPickerViewModel.kt b/AnkiDroid/src/main/java/com/ichi2/anki/deckpicker/DeckPickerViewModel.kt index 3fd85b345df7..fcdfe70af170 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/deckpicker/DeckPickerViewModel.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/deckpicker/DeckPickerViewModel.kt @@ -56,6 +56,7 @@ import com.ichi2.anki.reviewreminders.ScheduleRemindersDestination import com.ichi2.anki.settings.Prefs import com.ichi2.anki.syncAuth import com.ichi2.anki.utils.Destination +import com.ichi2.anki.utils.ext.allDecksCountsUncapped import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableSharedFlow @@ -166,10 +167,14 @@ class DeckPickerViewModel : tree.onlyHasDefaultDeck() && noCards }.stateIn(viewModelScope, SharingStarted.Eagerly, initialValue = null) + // The deck-tree protobuf clamps each node's new/review/learn counts at 9999 + // (backend behavior, shared with Anki Desktop), so summing the root's three + // fields underreports for large collections — see issue #17605. Route through + // `allDecksCountsUncapped` (uncapped, limit-aware via getQueuedCards) instead. val flowOfCardsDue = combine(flowOfDeckDueTree, flowOfDeckListInInitialState) { tree, inInitialState -> if (tree == null || inInitialState != false) return@combine null - tree.newCount + tree.revCount + tree.lrnCount + withCol { sched.allDecksCountsUncapped().count() } } /** "Studied N cards in 0 seconds today */ diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/Scheduler.kt b/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/Scheduler.kt index 233d81e9b2bc..f56a8c341c07 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/Scheduler.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/utils/ext/Scheduler.kt @@ -45,3 +45,30 @@ fun Scheduler.allDecksCounts(): Counts { } return total } + +/** + * Like [allDecksCounts] but uncapped: per-node counts on the deck-tree protobuf + * are clamped at 9999 by the backend, so summing them produces a wrong total + * for large collections (issue #17605). This iterates top-level decks and pulls + * each subtree's count from [Scheduler.counts] instead, which sources from + * `getQueuedCards` and is both limit-aware and unclamped — the same source + * `StudyOptionsFragment` uses to display per-deck counts. + * + * Side effect: temporarily mutates and restores `decks.selected()`. + */ +fun Scheduler.allDecksCountsUncapped(): Counts { + val total = Counts() + val previouslySelected = col.decks.selected() + try { + for (node in deckDueTree().children) { + col.decks.select(node.did) + val c = counts() + total.addNew(c.new) + total.addLrn(c.lrn) + total.addRev(c.rev) + } + } finally { + col.decks.select(previouslySelected) + } + return total +}