Skip to content

Commit 0f8fd6e

Browse files
committed
refactor(auto-advance): remove ViewModel dependency
that makes easier to test the class
1 parent 9e9053f commit 0f8fd6e

File tree

5 files changed

+64
-33
lines changed

5 files changed

+64
-33
lines changed

AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerViewModel.kt

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.ichi2.anki.ui.windows.reviewer
1717

1818
import androidx.lifecycle.SavedStateHandle
19+
import androidx.lifecycle.viewModelScope
1920
import anki.collection.OpChanges
2021
import anki.frontend.SetSchedulingStatesRequest
2122
import anki.scheduler.CardAnswer.Rating
@@ -60,7 +61,10 @@ import com.ichi2.anki.servicelayer.isSuspendNoteAvailable
6061
import com.ichi2.anki.settings.Prefs
6162
import com.ichi2.anki.tryRedo
6263
import com.ichi2.anki.tryUndo
64+
import com.ichi2.anki.ui.windows.reviewer.autoadvance.AnswerAction
6365
import com.ichi2.anki.ui.windows.reviewer.autoadvance.AutoAdvance
66+
import com.ichi2.anki.ui.windows.reviewer.autoadvance.AutoAdvanceAction
67+
import com.ichi2.anki.ui.windows.reviewer.autoadvance.QuestionAction
6468
import com.ichi2.anki.utils.CollectionPreferences
6569
import com.ichi2.anki.utils.Destination
6670
import com.ichi2.anki.utils.ext.answerCard
@@ -83,7 +87,8 @@ class ReviewerViewModel(
8387
val savedStateHandle: SavedStateHandle,
8488
) : CardViewerViewModel(),
8589
ChangeManager.Subscriber,
86-
BindingProcessor<ReviewerBinding, ViewerAction> {
90+
BindingProcessor<ReviewerBinding, ViewerAction>,
91+
AutoAdvance.ActionListener {
8792
private var queueState: Deferred<CurrentQueueState?> =
8893
asyncIO {
8994
withCol { sched.currentQueueState() }
@@ -122,7 +127,7 @@ class ReviewerViewModel(
122127
private val stateMutationKey = TimeManager.time.intTimeMS().toString()
123128
private var typedAnswer = ""
124129

125-
private val autoAdvance = AutoAdvance(this)
130+
private val autoAdvance = AutoAdvance(viewModelScope, this, currentCard)
126131
private val isHtmlTypeAnswerEnabled = Prefs.isHtmlTypeAnswerEnabled
127132
val answerTimer = AnswerTimer()
128133

@@ -716,6 +721,18 @@ class ReviewerViewModel(
716721
return true
717722
}
718723

724+
override suspend fun onAutoAdvanceAction(action: AutoAdvanceAction) {
725+
when (action) {
726+
QuestionAction.SHOW_ANSWER -> onShowAnswer()
727+
QuestionAction.SHOW_REMINDER -> actionFeedbackFlow.emit(TR.studyingQuestionTimeElapsed())
728+
AnswerAction.BURY_CARD -> buryCard()
729+
AnswerAction.ANSWER_AGAIN -> answerCard(Rating.AGAIN)
730+
AnswerAction.ANSWER_GOOD -> answerCard(Rating.GOOD)
731+
AnswerAction.ANSWER_HARD -> answerCard(Rating.HARD)
732+
AnswerAction.SHOW_REMINDER -> actionFeedbackFlow.emit(TR.studyingAnswerTimeElapsed())
733+
}
734+
}
735+
719736
// Based in https://github.com/ankitects/anki/blob/1f95d030bbc7ebcc004ffe1e2be2a320c9fe1e94/qt/aqt/reviewer.py#L201
720737
// and https://github.com/ankitects/anki/blob/1f95d030bbc7ebcc004ffe1e2be2a320c9fe1e94/qt/aqt/reviewer.py#L219
721738
override fun opExecuted(

AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/autoadvance/AnswerAction.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import com.ichi2.anki.libanki.DeckConfig.Companion.ANSWER_ACTION
2020

2121
enum class AnswerAction(
2222
val code: Int,
23-
) {
23+
) : AutoAdvanceAction {
2424
BURY_CARD(0),
2525
ANSWER_AGAIN(1),
2626
ANSWER_GOOD(2),

AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/autoadvance/AutoAdvance.kt

Lines changed: 21 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,13 @@
1515
*/
1616
package com.ichi2.anki.ui.windows.reviewer.autoadvance
1717

18-
import anki.scheduler.CardAnswer.Rating
19-
import com.ichi2.anki.CollectionManager.TR
2018
import com.ichi2.anki.asyncIO
21-
import com.ichi2.anki.launchCatchingIO
2219
import com.ichi2.anki.libanki.Card
23-
import com.ichi2.anki.reviewer.AutomaticAnswerAction
24-
import com.ichi2.anki.ui.windows.reviewer.ReviewerViewModel
20+
import kotlinx.coroutines.CoroutineScope
21+
import kotlinx.coroutines.Deferred
2522
import kotlinx.coroutines.Job
2623
import kotlinx.coroutines.delay
24+
import kotlinx.coroutines.launch
2725

2826
/**
2927
* Implementation of the `Auto Advance` deck options
@@ -36,8 +34,18 @@ import kotlinx.coroutines.delay
3634
* @see AutoAdvanceSettings
3735
*/
3836
class AutoAdvance(
39-
val viewModel: ReviewerViewModel,
37+
private val scope: CoroutineScope,
38+
private val listener: ActionListener,
39+
initialCard: Deferred<Card>,
4040
) {
41+
/**
42+
* Listens to the `Auto Advance` actions set in Deck options,
43+
* which can be either a [QuestionAction] or a [AnswerAction].
44+
*/
45+
fun interface ActionListener {
46+
suspend fun onAutoAdvanceAction(action: AutoAdvanceAction)
47+
}
48+
4149
var isEnabled = false
4250
set(value) {
4351
field = value
@@ -49,9 +57,8 @@ class AutoAdvance(
4957
private var answerActionJob: Job? = null
5058

5159
private var settings =
52-
viewModel.asyncIO {
53-
val card = viewModel.currentCard.await()
54-
AutoAdvanceSettings.createInstance(card.currentDeckId())
60+
scope.asyncIO {
61+
AutoAdvanceSettings.createInstance(initialCard.await().currentDeckId())
5562
}
5663

5764
private suspend fun durationToShowQuestionFor() = settings.await().durationToShowQuestionFor
@@ -72,7 +79,7 @@ class AutoAdvance(
7279
fun onCardChange(card: Card) {
7380
cancelQuestionAndAnswerActionJobs()
7481
settings =
75-
viewModel.asyncIO {
82+
scope.asyncIO {
7683
AutoAdvanceSettings.createInstance(card.currentDeckId())
7784
}
7885
}
@@ -82,12 +89,9 @@ class AutoAdvance(
8289
if (!durationToShowQuestionFor().isPositive() || !isEnabled) return
8390

8491
questionActionJob =
85-
viewModel.launchCatchingIO {
92+
scope.launch {
8693
delay(durationToShowQuestionFor())
87-
when (questionAction()) {
88-
QuestionAction.SHOW_ANSWER -> viewModel.onShowAnswer()
89-
QuestionAction.SHOW_REMINDER -> showReminder(TR.studyingQuestionTimeElapsed())
90-
}
94+
listener.onAutoAdvanceAction(questionAction())
9195
}
9296
}
9397

@@ -96,21 +100,9 @@ class AutoAdvance(
96100
if (!durationToShowAnswerFor().isPositive() || !isEnabled) return
97101

98102
answerActionJob =
99-
viewModel.launchCatchingIO {
103+
scope.launch {
100104
delay(durationToShowAnswerFor())
101-
when (answerAction()) {
102-
AnswerAction.BURY_CARD -> viewModel.buryCard()
103-
AnswerAction.ANSWER_AGAIN -> viewModel.answerCard(Rating.AGAIN)
104-
AnswerAction.ANSWER_HARD -> viewModel.answerCard(Rating.HARD)
105-
AnswerAction.ANSWER_GOOD -> viewModel.answerCard(Rating.GOOD)
106-
AnswerAction.SHOW_REMINDER -> showReminder(TR.studyingAnswerTimeElapsed())
107-
}
105+
listener.onAutoAdvanceAction(answerAction())
108106
}
109107
}
110-
111-
private fun showReminder(message: String) {
112-
viewModel.launchCatchingIO {
113-
viewModel.actionFeedbackFlow.emit(message)
114-
}
115-
}
116108
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/*
2+
* Copyright (c) 2025 Brayan Oliveira <69634269+brayandso@users.noreply.github.com>
3+
*
4+
* This program is free software; you can redistribute it and/or modify it under
5+
* the terms of the GNU General Public License as published by the Free Software
6+
* Foundation; either version 3 of the License, or (at your option) any later
7+
* version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY
10+
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
11+
* PARTICULAR PURPOSE. See the GNU General Public License for more details.
12+
*
13+
* You should have received a copy of the GNU General Public License along with
14+
* this program. If not, see <http://www.gnu.org/licenses/>.
15+
*/
16+
package com.ichi2.anki.ui.windows.reviewer.autoadvance
17+
18+
/**
19+
* Common interface for all actions triggered by Auto Advance.
20+
* Implemented by [QuestionAction] and [AnswerAction].
21+
*/
22+
sealed interface AutoAdvanceAction

AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/autoadvance/QuestionAction.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import com.ichi2.anki.libanki.DeckConfig.Companion.QUESTION_ACTION
2020

2121
enum class QuestionAction(
2222
val code: Int,
23-
) {
23+
) : AutoAdvanceAction {
2424
SHOW_ANSWER(0),
2525
SHOW_REMINDER(1),
2626
;

0 commit comments

Comments
 (0)