Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ import org.json.JSONObject
/** Deck completion uses Anki's daily limits, including subdecks and learning later today. */
internal object AnkiquestDecks {
private const val DAY_MS = 86_400_000L
const val DECK_STATUS_KEY = "ankiquestDeckStatus"

/** Whether the deck list marks decks finished today and learning due later. */
fun statusInDeckList(): Boolean =
com.ichi2.anki.AnkiDroidApp
.sharedPrefs()
.getBoolean(DECK_STATUS_KEY, true)

fun dailyStates(
col: Collection,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -415,13 +415,19 @@ class DeckPickerViewModel :
Pair(sched.deckDueTree(), isEmpty)
}
dailyDeckStates.value =
withCol {
com.ichi2.anki.ankiquest.AnkiquestDecks.dailyStates(
this,
com.ichi2.anki.common.time.TimeManager.time
.intTimeMS(),
deckDueTree,
)
if (com.ichi2.anki.ankiquest.AnkiquestDecks
.statusInDeckList()
) {
withCol {
com.ichi2.anki.ankiquest.AnkiquestDecks.dailyStates(
this,
com.ichi2.anki.common.time.TimeManager.time
.intTimeMS(),
deckDueTree,
)
}
} else {
emptyMap()
}
dueTree = deckDueTree

Expand Down
15 changes: 14 additions & 1 deletion AnkiDroid/src/main/java/com/ichi2/anki/widgets/DeckAdapter.kt
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,20 @@ class DeckAdapter(
binding.deckName.text = node.lastDeckNameComponent
binding.deckName.setTextColor(if (node.filtered) deckNameDynColor else deckNameDefaultColor)

binding.deckDailyStatus.isVisible = node.dailyState != null
val dailyStatus = node.dailyState?.status
binding.deckDailyStatus.isVisible = dailyStatus == com.ichi2.anki.ankiquest.DailyDeckStatus.LEARNING_LATER
val finished = dailyStatus == com.ichi2.anki.ankiquest.DailyDeckStatus.DONE
binding.deckName.setCompoundDrawablesRelativeWithIntrinsicBounds(0, 0, if (finished) R.drawable.ic_done else 0, 0)
TextViewCompat.setCompoundDrawableTintList(
binding.deckName,
if (finished) {
android.content.res.ColorStateList
.valueOf(binding.deckName.context.getColor(R.color.aq_deck_green))
} else {
null
},
)
binding.deckName.compoundDrawablePadding = 4.dp.toPx(binding.deckName.context)
node.dailyState?.let { daily ->
val status = binding.deckDailyStatus
status.text = daily.label(status.context)
Expand Down
2 changes: 2 additions & 0 deletions AnkiDroid/src/main/res/values-de/ankiquest_daily_decks.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
<resources>
<string name="ankiquest_deck_status_title">Tagesstatus in der Stapelliste</string>
<string name="ankiquest_deck_status_summary">Markiert heute erledigte Stapel und später heute fälliges Lernen</string>
<string name="aq_deck_later">Lernen später heute · nächste Wiederholung um %1$s</string>
<string name="aq_deck_done">Für heute erledigt</string>
<string name="aq_deck_nothing">Heute nichts geplant</string>
Expand Down
2 changes: 2 additions & 0 deletions AnkiDroid/src/main/res/values-es/ankiquest_daily_decks.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
<resources>
<string name="ankiquest_deck_status_title">Estado diario en la lista de mazos</string>
<string name="ankiquest_deck_status_summary">Marca los mazos terminados hoy y el aprendizaje pendiente para más tarde</string>
<string name="aq_deck_later">Aprendizaje pendiente hoy · siguiente a las %1$s</string>
<string name="aq_deck_done">Terminado por hoy</string>
<string name="aq_deck_nothing">Nada programado para hoy</string>
Expand Down
2 changes: 2 additions & 0 deletions AnkiDroid/src/main/res/values-fr/ankiquest_daily_decks.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
<resources>
<string name="ankiquest_deck_status_title">État du jour dans la liste des paquets</string>
<string name="ankiquest_deck_status_summary">Signale les paquets terminés aujourd’hui et l’apprentissage prévu plus tard</string>
<string name="aq_deck_later">Apprentissage plus tard aujourd’hui · prochaine révision à %1$s</string>
<string name="aq_deck_done">Terminé pour aujourd’hui</string>
<string name="aq_deck_nothing">Rien de prévu aujourd’hui</string>
Expand Down
2 changes: 2 additions & 0 deletions AnkiDroid/src/main/res/values-pt/ankiquest_daily_decks.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
<resources>
<string name="ankiquest_deck_status_title">Estado diário na lista de baralhos</string>
<string name="ankiquest_deck_status_summary">Marca os baralhos concluídos hoje e a aprendizagem pendente para mais tarde</string>
<string name="aq_deck_later">Aprendizagem mais tarde hoje · próxima revisão às %1$s</string>
<string name="aq_deck_done">Concluído por hoje</string>
<string name="aq_deck_nothing">Nada agendado para hoje</string>
Expand Down
3 changes: 3 additions & 0 deletions AnkiDroid/src/main/res/values/ankiquest_daily_decks.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- SPDX-License-Identifier: AGPL-3.0-only -->
<resources>
<string name="ankiquest_deck_status_key" translatable="false">ankiquestDeckStatus</string>
<string name="ankiquest_deck_status_title" maxLength="41">Daily status in the deck list</string>
<string name="ankiquest_deck_status_summary">Mark decks finished today, and learning due later today</string>
<string name="aq_deck_later">Learning later today · next at %1$s</string>
<string name="aq_deck_done">Done today</string>
<string name="aq_deck_nothing">Nothing scheduled today</string>
Expand Down
5 changes: 5 additions & 0 deletions AnkiDroid/src/main/res/xml/preferences_ankiquest_study.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@
android:key="@string/ankiquest_summary_preference_key"
android:summary="@string/ankiquest_summary_preference_summary"
android:title="@string/ankiquest_summary_preference_title" />
<SwitchPreferenceCompat
android:defaultValue="true"
android:key="@string/ankiquest_deck_status_key"
android:summary="@string/ankiquest_deck_status_summary"
android:title="@string/ankiquest_deck_status_title" />
<SwitchPreferenceCompat
android:key="@string/ankiquest_streak_protection_key"
android:persistent="false"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ class PreferencesAnalyticsTest : RobolectricTest() {
R.string.ankiquest_upload_all_key,
R.string.ankiquest_check_updates_key,
R.string.ankiquest_update_channel_key,
R.string.ankiquest_deck_status_key,
R.string.ankiquest_deck_notifications_key,
R.string.ankiquest_avatar_key,
R.string.ankiquest_companion_key,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// SPDX-License-Identifier: AGPL-3.0-only

package com.ichi2.anki.ankiquest

import android.view.ContextThemeWrapper
import android.view.View
import androidx.core.content.edit
import androidx.core.view.isVisible
import androidx.recyclerview.widget.ConcatAdapter
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.ichi2.anki.AnkiDroidApp
import com.ichi2.anki.R
import com.ichi2.anki.RobolectricTest
import com.ichi2.anki.awaitInitialDeckHolder
import com.ichi2.anki.common.time.TimeManager
import com.ichi2.anki.deckpicker.DeckFilters
import com.ichi2.anki.deckpicker.filterAndFlattenDisplay
import com.ichi2.anki.libanki.DeckId
import com.ichi2.anki.widgets.DeckAdapter
import com.ichi2.anki.withDeckPicker
import org.junit.Test
import org.junit.runner.RunWith
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertTrue

@RunWith(AndroidJUnit4::class)
class AnkiquestDeckStatusTest : RobolectricTest() {
@Test
fun `only learning due later gets a second line, and a finished deck gets a check`() {
val done = addDeck("Done")
val ready = addDeck("Ready")
val later = addDeck("Later")
val empty = addDeck("Empty")
val states =
mapOf(
done to DailyDeckState(ready = 0, remaining = 0, reviewed = 3),
ready to DailyDeckState(ready = 5, remaining = 5, reviewed = 0),
later to DailyDeckState(ready = 0, remaining = 1, reviewed = 1, nextLearningAt = TimeManager.time.intTimeMS() + 3_600_000),
empty to DailyDeckState(ready = 0, remaining = 0, reviewed = 0),
)
val rows = bind(states)

assertTrue(
rows
.getValue(later)
.binding.deckDailyStatus.isVisible,
"learning later today is new information",
)
for (deck in listOf(done, ready, empty)) {
assertFalse(
rows
.getValue(deck)
.binding.deckDailyStatus.isVisible,
"the counts already say this",
)
}
assertNotNull(
rows
.getValue(done)
.binding.deckName.compoundDrawablesRelative[2],
"a finished deck shows a check",
)
for (deck in listOf(ready, later, empty)) {
assertNull(
rows
.getValue(deck)
.binding.deckName.compoundDrawablesRelative[2],
)
}
}

@Test
fun `the deck list shows daily status only while the setting is on`() {
val spanish = addDeck("Spanish")
col.decks.select(spanish)
for ((enabled, expected) in listOf(true to true, false to false)) {
AnkiDroidApp.sharedPrefs().edit { putBoolean(AnkiquestDecks.DECK_STATUS_KEY, enabled) }
withDeckPicker(deckCount = 0) { deckPicker ->
deckPicker.awaitInitialDeckHolder(spanish)
val adapter =
(deckPicker.deckPickerBinding.decks.adapter as ConcatAdapter)
.adapters
.filterIsInstance<DeckAdapter>()
.single()
assertEquals(expected, adapter.currentList.any { it.dailyState != null }, "setting on: $enabled")
}
}
}

private fun bind(states: Map<DeckId, DailyDeckState>): Map<DeckId, DeckAdapter.ViewHolder> {
val context = ContextThemeWrapper(targetContext, R.style.Theme_Light)
val adapter =
DeckAdapter(
context,
onDeckSelected = {},
onDeckCountsSelected = {},
onDeckChildrenToggled = {},
onDeckContextRequested = {},
onDeckRightClick = { _, _, _ -> },
)
val list =
RecyclerView(context).apply {
layoutManager = LinearLayoutManager(context)
this.adapter = adapter
}
val nodes =
col.sched
.deckDueTree()
.filterAndFlattenDisplay(DeckFilters.create(""), 1)
.map { it.withDailyState(states[it.did]) }
var committed = false
adapter.submit(nodes, hasSubDecks = false) { committed = true }
advanceRobolectricLooperUntil { committed }
list.measure(
View.MeasureSpec.makeMeasureSpec(480, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(2000, View.MeasureSpec.EXACTLY),
)
list.layout(0, 0, 480, 2000)
return states.keys.associateWith { deck ->
list.findViewHolderForAdapterPosition(adapter.currentList.indexOfFirst { it.did == deck }) as DeckAdapter.ViewHolder
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class AnkiquestSettingsMenuTest : RobolectricTest() {
R.string.ankiquest_test_key,
R.string.ankiquest_open_today_key,
R.string.ankiquest_summary_preference_key,
R.string.ankiquest_deck_status_key,
R.string.ankiquest_streak_protection_key,
R.string.ankiquest_notify_rank_key,
R.string.ankiquest_streak_hours_key,
Expand Down
Loading