0
0
mirror of https://github.com/ankidroid/Anki-Android.git synced 2024-09-20 12:02:16 +02:00

Remove some unnecessary DB queries when legacy schema disabled

- collapsed + filtered status is included in the deck list from the
backend, so does not need to be looked up for each row
- cache the current deck id at deck list build time, so it doesn't need
to be looked up for every row (applies to old schema too)
- skip default deck logic, which is handled by backend
This commit is contained in:
Damien Elmes 2022-06-25 11:32:58 +10:00 committed by Mike Hardy
parent 635a074472
commit 9644f5724c
7 changed files with 69 additions and 32 deletions

View File

@ -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<AbstractDeckTreeNode>) {
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<TreeNode<AbstractDeckTreeNode>>) {
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)
}
}
}

View File

@ -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<AbstractDeckTreeNode> {
private val mNameComponents: Array<String>

View File

@ -46,6 +46,8 @@ fun CollectionV16.deckTreeLegacy(includeCounts: Boolean): List<TreeNode<DeckDueT
node.reviewCount,
node.learnCount,
node.newCount,
collapsed = node.collapsed,
filtered = node.filtered
)
)
treeNode.children.addAll(node.childrenList.asSequence().map { toLegacyNode(it, thisName) })

View File

@ -36,7 +36,16 @@ import kotlin.math.min
@KotlinCleanup("maybe possible to remove gettres for revCount/lrnCount")
@KotlinCleanup("rename name -> 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",

View File

@ -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});
}

View File

@ -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<TreeNode<DeckDueTreeNode>> deckDueTree(@Nullable CancelListener cancelListener) {
if (!BackendFactory.getDefaultLegacySchema()) {
return BackendSchedKt.deckTreeLegacy(getCol().getNewBackend(), true);

View File

@ -112,16 +112,16 @@ public class SchedV2Test extends RobolectricTest {
// These matched the previous Java data
// These may want to be changed back
List<TreeNode<DeckDueTreeNode>> 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<DeckDueTreeNode> cazNode = new TreeNode<>(caz);