Skip to content

Commit bd1d65a

Browse files
committed
feat(study-screen): reset progress
1 parent 9459008 commit bd1d65a

5 files changed

Lines changed: 51 additions & 3 deletions

File tree

AnkiDroid/src/main/java/com/ichi2/anki/preferences/reviewer/ViewerAction.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ enum class ViewerAction(
7474
ADD_NOTE(R.id.action_add_note, R.drawable.ic_add, R.string.menu_add_note, DISABLED),
7575
TAG(R.id.action_edit_tags, R.drawable.ic_tag, R.string.menu_edit_tags, DISABLED),
7676
RESCHEDULE_NOTE(R.id.action_set_due_date, R.drawable.ic_reschedule, titleRes = R.string.empty_string, DISABLED),
77+
RESET_PROGRESS(
78+
R.id.action_reset_progress,
79+
drawableRes = R.drawable.ic_backup_restore,
80+
titleRes = R.string.card_editor_reset_card,
81+
DISABLED,
82+
),
7783
TOGGLE_AUTO_ADVANCE(R.id.action_toggle_auto_advance, R.drawable.ic_fast_forward, R.string.toggle_auto_advance, DISABLED),
7884
RECORD_VOICE(R.id.action_record_voice, R.drawable.ic_action_mic, R.string.record_voice, DISABLED),
7985
PLAY_MEDIA(R.id.action_replay_media, R.drawable.ic_play_circle_white, R.string.replay_media, DISABLED),
@@ -149,6 +155,7 @@ enum class ViewerAction(
149155
STATISTICS -> listOf(keycode(KeyEvent.KEYCODE_T))
150156
PLAY_MEDIA -> listOf(keycode(KeyEvent.KEYCODE_R))
151157
PREVIOUS_CARD_INFO -> listOf(keycode(KeyEvent.KEYCODE_I, ModifierKeys(shift = false, ctrl = true, alt = true)))
158+
RESET_PROGRESS -> listOf(keycode(KeyEvent.KEYCODE_N, ModifierKeys(ctrl = true, alt = true, shift = false)))
152159
TOGGLE_FLAG_RED ->
153160
listOf(
154161
keycode(KeyEvent.KEYCODE_1, ctrl()),

AnkiDroid/src/main/java/com/ichi2/anki/scheduling/ForgetCardsDialog.kt

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@ import android.os.Bundle
2121
import androidx.core.content.edit
2222
import androidx.core.os.bundleOf
2323
import androidx.fragment.app.DialogFragment
24+
import androidx.fragment.app.Fragment
25+
import androidx.fragment.app.FragmentActivity
26+
import androidx.fragment.app.setFragmentResultListener
2427
import com.google.android.material.dialog.MaterialAlertDialogBuilder
2528
import com.google.android.material.snackbar.Snackbar
26-
import com.ichi2.anki.AnkiActivity
2729
import com.ichi2.anki.CollectionManager.TR
2830
import com.ichi2.anki.R
2931
import com.ichi2.anki.common.annotations.NeedsTest
@@ -155,7 +157,7 @@ class ForgetCardsDialog : DialogFragment() {
155157
*
156158
* @param cardsIdsProducer lambda which returns the list of cards for which to reset the progress
157159
*/
158-
internal fun AnkiActivity.registerOnForgetHandler(cardsIdsProducer: suspend () -> List<CardId>) {
160+
internal fun FragmentActivity.registerOnForgetHandler(cardsIdsProducer: suspend () -> List<CardId>) {
159161
setFragmentResultListener(ForgetCardsDialog.REQUEST_KEY_FORGET) { _, bundle ->
160162
forgetCards(
161163
cardsIdsProducer = cardsIdsProducer,
@@ -165,7 +167,23 @@ internal fun AnkiActivity.registerOnForgetHandler(cardsIdsProducer: suspend () -
165167
}
166168
}
167169

168-
private fun AnkiActivity.forgetCards(
170+
/**
171+
* Listen for requests from [ForgetCardsDialog] and triggers backend calls to reset progress for
172+
* current selected/reviewed card/cards. Callers need to supply the list of cards ids.
173+
*
174+
* @param cardsIdsProducer lambda which returns the list of cards for which to reset the progress
175+
*/
176+
internal fun Fragment.registerOnForgetHandler(cardsIdsProducer: suspend () -> List<CardId>) {
177+
setFragmentResultListener(ForgetCardsDialog.REQUEST_KEY_FORGET) { _, bundle ->
178+
activity?.forgetCards(
179+
cardsIdsProducer = cardsIdsProducer,
180+
restoreOriginalPositionIfPossible = bundle.getBoolean(ForgetCardsDialog.ARG_RESTORE_ORIGINAL),
181+
resetRepetitionAndLapseCounts = bundle.getBoolean(ForgetCardsDialog.ARG_RESET_REPETITION),
182+
)
183+
}
184+
}
185+
186+
private fun FragmentActivity.forgetCards(
169187
cardsIdsProducer: suspend () -> List<CardId>,
170188
restoreOriginalPositionIfPossible: Boolean,
171189
resetRepetitionAndLapseCounts: Boolean,

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,9 @@ import com.ichi2.anki.previewer.setFrameStyle
7070
import com.ichi2.anki.previewer.stdHtml
7171
import com.ichi2.anki.reviewer.BindingMap
7272
import com.ichi2.anki.reviewer.ReviewerBinding
73+
import com.ichi2.anki.scheduling.ForgetCardsDialog
7374
import com.ichi2.anki.scheduling.SetDueDateDialog
75+
import com.ichi2.anki.scheduling.registerOnForgetHandler
7476
import com.ichi2.anki.settings.Prefs
7577
import com.ichi2.anki.settings.enums.FrameStyle
7678
import com.ichi2.anki.settings.enums.HideSystemBars
@@ -94,6 +96,8 @@ import com.squareup.seismic.ShakeDetector
9496
import dev.androidbroadcast.vbpd.viewBinding
9597
import kotlinx.coroutines.Job
9698
import kotlinx.coroutines.delay
99+
import kotlinx.coroutines.flow.launchIn
100+
import kotlinx.coroutines.flow.onEach
97101
import kotlinx.coroutines.launch
98102
import timber.log.Timber
99103
import kotlin.math.max
@@ -176,6 +180,7 @@ class ReviewerFragment :
176180
setupToolbarPosition()
177181
setupAnswerTimer()
178182
setupMargins()
183+
setupResetProgress()
179184
setupCheckPronunciation()
180185
setupActions()
181186
setupWhiteboard()
@@ -499,6 +504,17 @@ class ReviewerFragment :
499504
}
500505
}
501506

507+
private fun setupResetProgress() {
508+
viewModel.resetProgressFlow
509+
.flowWithLifecycle(lifecycle)
510+
.onEach {
511+
showDialogFragment(ForgetCardsDialog())
512+
}.launchIn(lifecycleScope)
513+
// TODO handle 'Reset progress' in the ViewModel instead of the activity, once
514+
// a mechanism of showing a progress bar if the operation takes too long is implemented
515+
registerOnForgetHandler { listOf(viewModel.getCardId()) }
516+
}
517+
502518
private fun setupCheckPronunciation() {
503519
viewModel.voiceRecorderEnabledFlow.flowWithLifecycle(lifecycle).collectIn(lifecycleScope) { isEnabled ->
504520
if (isEnabled && binding.checkPronunciationContainer.getFragment<CheckPronunciationFragment?>() == null) {

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ class ReviewerViewModel(
114114
val destinationFlow = MutableSharedFlow<Destination>()
115115
val editNoteTagsFlow = MutableSharedFlow<NoteId>()
116116
val setDueDateFlow = MutableSharedFlow<CardId>()
117+
val resetProgressFlow = MutableSharedFlow<Unit>()
117118
val answerFeedbackFlow = MutableSharedFlow<Rating>()
118119
val voiceRecorderEnabledFlow = MutableStateFlow(false)
119120
val whiteboardEnabledFlow = MutableStateFlow(repository.isWhiteboardEnabled)
@@ -200,6 +201,8 @@ class ReviewerViewModel(
200201
executeAction(action)
201202
}
202203

204+
suspend fun getCardId() = currentCard.await().id
205+
203206
/**
204207
* Sends an [eval] request to load the card answer, and updates components
205208
* with behavior specific to the `Answer` card side.
@@ -659,6 +662,8 @@ class ReviewerViewModel(
659662
setDueDateFlow.emit(cardId)
660663
}
661664

665+
private suspend fun launchResetProgress() = resetProgressFlow.emit(Unit)
666+
662667
private suspend fun setupAnswerTimer(card: Card) {
663668
val shouldShowTimer = withCol { card.shouldShowTimer(this@withCol) }
664669
val limitMs = withCol { card.timeLimit(this@withCol) }
@@ -692,6 +697,7 @@ class ReviewerViewModel(
692697
ViewerAction.REDO -> redo()
693698
ViewerAction.UNDO -> undo()
694699
ViewerAction.RESCHEDULE_NOTE -> launchSetDueDate()
700+
ViewerAction.RESET_PROGRESS -> launchResetProgress()
695701
ViewerAction.TOGGLE_AUTO_ADVANCE -> toggleAutoAdvance()
696702
ViewerAction.BURY_NOTE -> buryNote()
697703
ViewerAction.BURY_CARD -> buryCard()

AnkiDroid/src/main/res/values/ids.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
<item type="id" name="action_record_voice"/>
4545
<item type="id" name="action_replay_media"/>
4646
<item type="id" name="action_set_due_date"/>
47+
<item type="id" name="action_reset_progress"/>
4748
<item type="id" name="action_browse"/>
4849
<item type="id" name="action_statistics"/>
4950

0 commit comments

Comments
 (0)