diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/widgets/DeckAdapter.kt b/AnkiDroid/src/main/java/com/ichi2/anki/widgets/DeckAdapter.kt index 1f56d76d0c..8c80ff0683 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/widgets/DeckAdapter.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/widgets/DeckAdapter.kt @@ -35,6 +35,7 @@ import com.ichi2.libanki.sched.Counts import com.ichi2.libanki.sched.TreeNode import com.ichi2.utils.KotlinCleanup import com.ichi2.utils.TypedFilter +import net.ankiweb.rsdroid.BackendFactory import java.util.* @KotlinCleanup("lots to do") @@ -52,6 +53,7 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context) private val mExpandImage: Drawable? private val mCollapseImage: Drawable? private val mNoExpander: Drawable = ColorDrawable(Color.TRANSPARENT) + private var currentDeckId: Long = 0 // Listeners private var mDeckClickListener: View.OnClickListener? = null @@ -129,6 +131,7 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context) mNew = mLrn mNumbersComputed = true mHasSubdecks = false + currentDeckId = mCol.decks.current().optLong("id") processNodes(nodes) // Filtering performs notifyDataSetChanged after the async work is complete getFilter().filter(filter) @@ -185,7 +188,12 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context) } // Set deck name and colour. Filtered decks have their own colour holder.deckName.text = node.lastDeckNameComponent - if (mCol.decks.isDyn(node.did)) { + val filtered = if (!BackendFactory.defaultLegacySchema) { + node.filtered + } else { + mCol.decks.isDyn(node.did) + } + if (filtered) { holder.deckName.setTextColor(mDeckNameDynColor) } else { holder.deckName.setTextColor(mDeckNameDefaultColor) @@ -218,7 +226,7 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context) } private fun isCurrentlySelectedDeck(node: AbstractDeckTreeNode): Boolean { - return node.did == mCol.decks.current().optLong("id") + return node.did == currentDeckId } override fun getItemCount(): Int { @@ -227,7 +235,11 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context) private fun setDeckExpander(expander: ImageButton, indent: ImageButton, node: TreeNode) { val nodeValue = node.value - val collapsed = mCol.decks.get(nodeValue.did).optBoolean("collapsed", false) + val collapsed = if (BackendFactory.defaultLegacySchema) { + mCol.decks.get(nodeValue.did).optBoolean("collapsed", false) + } else { + node.value.collapsed + } // Apply the correct expand/collapse drawable if (node.hasChildren()) { expander.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES @@ -249,20 +261,30 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context) private fun processNodes(nodes: List>) { for (node in nodes) { - // If the default deck is empty, hide it by not adding it to the deck list. - // We don't hide it if it's the only deck or if it has sub-decks. - if (node.value.did == 1L && nodes.size > 1 && !node.hasChildren()) { - if (!defaultDeckHasCards(mCol)) { - continue - } - } - // If any of this node's parents are collapsed, don't add it to the deck list - for (parent in mCol.decks.parents(node.value.did)) { - mHasSubdecks = true // If a deck has a parent it means it's a subdeck so set a flag - if (parent.optBoolean("collapsed")) { - return + var shouldRecurse = true + if (BackendFactory.defaultLegacySchema) { + // If the default deck is empty, hide it by not adding it to the deck list. + // We don't hide it if it's the only deck or if it has sub-decks. + if (node.value.did == 1L && nodes.size > 1 && !node.hasChildren()) { + if (!defaultDeckHasCards(mCol)) { + continue + } + } + // If any of this node's parents are collapsed, don't add it to the deck list + for (parent in mCol.decks.parents(node.value.did)) { + mHasSubdecks = true // If a deck has a parent it means it's a subdeck so set a flag + if (parent.optBoolean("collapsed")) { + return + } + } + } else { + // backend takes care of excluding default, and includes collapsed info + if (node.value.collapsed) { + mHasSubdecks = true + shouldRecurse = false } } + mDeckList.add(node) mCurrentDeckList.add(node) @@ -275,7 +297,9 @@ class DeckAdapter(private val layoutInflater: LayoutInflater, context: Context) } } // Process sub-decks - processNodes(node.children) + if (shouldRecurse) { + processNodes(node.children) + } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/AbstractDeckTreeNode.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/AbstractDeckTreeNode.kt index 3237e7fe1d..694545111a 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/AbstractDeckTreeNode.kt +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/AbstractDeckTreeNode.kt @@ -36,7 +36,10 @@ abstract class AbstractDeckTreeNode( * @return The full deck name, e.g. "A::B::C" */ val fullDeckName: String, - val did: Long + val did: Long, + // only set when new backend active + open var collapsed: Boolean = false, + open var filtered: Boolean = false ) : Comparable { private val mNameComponents: Array diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/BackendSched.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/BackendSched.kt index a65ad5f97f..03970bd655 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/BackendSched.kt +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/BackendSched.kt @@ -46,6 +46,8 @@ fun CollectionV16.deckTreeLegacy(includeCounts: Boolean): List fullDeckName") @RustCleanup("after migration, consider dropping this and using backend tree structure directly") -class DeckDueTreeNode(name: String, did: Long, override var revCount: Int, override var lrnCount: Int, override var newCount: Int) : AbstractDeckTreeNode(name, did) { +class DeckDueTreeNode( + name: String, + did: Long, + override var revCount: Int, + override var lrnCount: Int, + override var newCount: Int, + // only set when defaultLegacySchema is false + override var collapsed: Boolean = false, + override var filtered: Boolean = false +) : AbstractDeckTreeNode(name, did, collapsed, filtered) { override fun toString(): String { return String.format( Locale.US, "%s, %d, %d, %d, %d", diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Sched.java b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Sched.java index c4f0abea3f..6568318b38 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Sched.java +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Sched.java @@ -242,7 +242,7 @@ public class Sched extends SchedV2 { // reviews int rev = _revForDeck(deck.getLong("id"), rlim); // save to list - deckNodes.add(new DeckDueTreeNode(deck.getString("name"), deck.getLong("id"), rev, lrn, _new)); + deckNodes.add(new DeckDueTreeNode(deck.getString("name"), deck.getLong("id"), rev, lrn, _new, false, false)); // add deck as a parent lims.put(Decks.normalizeName(deck.getString("name")), new Integer[]{nlim, rlim}); } diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/SchedV2.java b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/SchedV2.java index 0c1e369b81..0f8832c534 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/SchedV2.java +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/SchedV2.java @@ -563,7 +563,7 @@ public class SchedV2 extends AbstractSched { int rlim = _deckRevLimitSingle(deck, plim, false); int rev = _revForDeck(deck.getLong("id"), rlim, childMap); // save to list - deckNodes.add(new DeckDueTreeNode(deck.getString("name"), deck.getLong("id"), rev, lrn, _new)); + deckNodes.add(new DeckDueTreeNode(deck.getString("name"), deck.getLong("id"), rev, lrn, _new, false, false)); // add deck as a parent lims.put(Decks.normalizeName(deck.getString("name")), new Integer[]{nlim, rlim}); } @@ -593,8 +593,7 @@ public class SchedV2 extends AbstractSched { } @Nullable - @RustCleanup("enable for v2 once backend is updated to 2.1.41+") - @RustCleanup("once both v1 and v2 are using backend, cancelListener can be removed") + @RustCleanup("once defaultLegacySchema is removed, cancelListener can be removed") public List> deckDueTree(@Nullable CancelListener cancelListener) { if (!BackendFactory.getDefaultLegacySchema()) { return BackendSchedKt.deckTreeLegacy(getCol().getNewBackend(), true); diff --git a/AnkiDroid/src/test/java/com/ichi2/libanki/sched/SchedV2Test.java b/AnkiDroid/src/test/java/com/ichi2/libanki/sched/SchedV2Test.java index 1a1c2d483a..dabe67e3be 100644 --- a/AnkiDroid/src/test/java/com/ichi2/libanki/sched/SchedV2Test.java +++ b/AnkiDroid/src/test/java/com/ichi2/libanki/sched/SchedV2Test.java @@ -112,16 +112,16 @@ public class SchedV2Test extends RobolectricTest { // These matched the previous Java data // These may want to be changed back List> expected = new ArrayList<>(); - DeckDueTreeNode caz = new DeckDueTreeNode("cmxieunwoogyxsctnjmv::abcdefgh::ZYXW", 1596783600480L, 0, 0, 0); - DeckDueTreeNode ca = new DeckDueTreeNode("cmxieunwoogyxsctnjmv::abcdefgh", 1596783600460L, 0, 0, 0); - DeckDueTreeNode ci = new DeckDueTreeNode("cmxieunwoogyxsctnjmv::INSBGDS", 1596783600500L, 0, 0, 0); - DeckDueTreeNode c = new DeckDueTreeNode("cmxieunwoogyxsctnjmv", 1596783600440L, 0, 0, 0); - DeckDueTreeNode defaul = new DeckDueTreeNode("Default", 1, 0, 0, 0); - DeckDueTreeNode s = new DeckDueTreeNode("scxipjiyozczaaczoawo", 1596783600420L, 0, 0, 0); - DeckDueTreeNode f = new DeckDueTreeNode("blank::foobar", 1596783600540L, 0, 0, 0); - DeckDueTreeNode b = new DeckDueTreeNode("blank", 1596783600520L, 0, 0, 0); - DeckDueTreeNode aBlank = new DeckDueTreeNode("A::blank", 1596783600580L, 0, 0, 0); - DeckDueTreeNode a = new DeckDueTreeNode("A", 1596783600560L, 0, 0, 0); + DeckDueTreeNode caz = new DeckDueTreeNode("cmxieunwoogyxsctnjmv::abcdefgh::ZYXW", 1596783600480L, 0, 0, 0, false, false); + DeckDueTreeNode ca = new DeckDueTreeNode("cmxieunwoogyxsctnjmv::abcdefgh", 1596783600460L, 0, 0, 0, false, false); + DeckDueTreeNode ci = new DeckDueTreeNode("cmxieunwoogyxsctnjmv::INSBGDS", 1596783600500L, 0, 0, 0, false, false); + DeckDueTreeNode c = new DeckDueTreeNode("cmxieunwoogyxsctnjmv", 1596783600440L, 0, 0, 0, false, false); + DeckDueTreeNode defaul = new DeckDueTreeNode("Default", 1, 0, 0, 0, false, false); + DeckDueTreeNode s = new DeckDueTreeNode("scxipjiyozczaaczoawo", 1596783600420L, 0, 0, 0, false, false); + DeckDueTreeNode f = new DeckDueTreeNode("blank::foobar", 1596783600540L, 0, 0, 0, false, false); + DeckDueTreeNode b = new DeckDueTreeNode("blank", 1596783600520L, 0, 0, 0, false, false); + DeckDueTreeNode aBlank = new DeckDueTreeNode("A::blank", 1596783600580L, 0, 0, 0, false, false); + DeckDueTreeNode a = new DeckDueTreeNode("A", 1596783600560L, 0, 0, 0, false, false); TreeNode cazNode = new TreeNode<>(caz);