From 89cdbab877aba7166e03ec396cac6a576a0fd7a5 Mon Sep 17 00:00:00 2001 From: Brayan Oliveira <69634269+brayandso@users.noreply.github.com> Date: Sun, 23 Jun 2024 19:22:13 -0300 Subject: [PATCH] feat(new reviewer): next times --- .../windows/reviewer/AnswerButtonsNextTime.kt | 33 +++++++++++++++++++ .../ui/windows/reviewer/ReviewerFragment.kt | 30 ++++++++++++----- .../ui/windows/reviewer/ReviewerViewModel.kt | 31 +++++++++++++++++ 3 files changed, 86 insertions(+), 8 deletions(-) create mode 100644 AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/AnswerButtonsNextTime.kt diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/AnswerButtonsNextTime.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/AnswerButtonsNextTime.kt new file mode 100644 index 000000000000..c83377a88cb5 --- /dev/null +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/AnswerButtonsNextTime.kt @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024 Brayan Oliveira + * + * This program is free software; you can redistribute it and/or modify it under + * the terms of the GNU General Public License as published by the Free Software + * Foundation; either version 3 of the License, or (at your option) any later + * version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY + * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A + * PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ +package com.ichi2.anki.ui.windows.reviewer + +import com.ichi2.anki.CollectionManager.withCol +import com.ichi2.libanki.sched.CurrentQueueState + +data class AnswerButtonsNextTime( + val again: String, + val hard: String, + val good: String, + val easy: String +) { + companion object { + suspend fun from(state: CurrentQueueState): AnswerButtonsNextTime { + val (again, hard, good, easy) = withCol { sched.describeNextStates(state.states) } + return AnswerButtonsNextTime(again = again, hard = hard, good = good, easy = easy) + } + } +} diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt index 3e46527dc926..d762d1ed1c86 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerFragment.kt @@ -23,6 +23,7 @@ import android.view.MenuItem import android.view.View import android.webkit.WebView import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.StringRes import androidx.appcompat.view.menu.MenuBuilder import androidx.appcompat.widget.ThemeUtils import androidx.appcompat.widget.Toolbar @@ -146,19 +147,32 @@ class ReviewerFragment : } private fun setupAnswerButtons(view: View) { - view.findViewById(R.id.again_button).setOnClickListener { - viewModel.answerAgain() + fun MaterialButton.setAnswerButtonNextTime(@StringRes title: Int, nextTime: String?) { + val titleString = context.getString(title) + text = ReviewerViewModel.buildAnswerButtonText(titleString, nextTime) } - view.findViewById(R.id.hard_button).setOnClickListener { - viewModel.answerHard() + + val againButton = view.findViewById(R.id.again_button).apply { + setOnClickListener { viewModel.answerAgain() } + } + val hardButton = view.findViewById(R.id.hard_button).apply { + setOnClickListener { viewModel.answerHard() } } - view.findViewById(R.id.good_button).setOnClickListener { - viewModel.answerGood() + val goodButton = view.findViewById(R.id.good_button).apply { + setOnClickListener { viewModel.answerGood() } } - view.findViewById(R.id.easy_button).setOnClickListener { - viewModel.answerEasy() + val easyButton = view.findViewById(R.id.easy_button).apply { + setOnClickListener { viewModel.answerEasy() } } + viewModel.answerButtonsNextTimeFlow.flowWithLifecycle(lifecycle) + .collectIn(lifecycleScope) { times -> + againButton.setAnswerButtonNextTime(R.string.ease_button_again, times?.again) + hardButton.setAnswerButtonNextTime(R.string.ease_button_hard, times?.hard) + goodButton.setAnswerButtonNextTime(R.string.ease_button_good, times?.good) + easyButton.setAnswerButtonNextTime(R.string.ease_button_easy, times?.easy) + } + val showAnswerButton = view.findViewById(R.id.show_answer).apply { setOnClickListener { viewModel.showAnswer() diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerViewModel.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerViewModel.kt index 02679dc28456..5f40abe1095c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerViewModel.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ui/windows/reviewer/ReviewerViewModel.kt @@ -15,6 +15,9 @@ */ package com.ichi2.anki.ui.windows.reviewer +import android.text.style.RelativeSizeSpan +import androidx.core.text.buildSpannedString +import androidx.core.text.inSpans import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory @@ -91,6 +94,11 @@ class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) : */ private var statesMutated = true + val answerButtonsNextTimeFlow: MutableStateFlow = MutableStateFlow(null) + private val shouldShowNextTimes: Deferred = asyncIO { + withCol { config.get("estTimes") ?: true } + } + init { ChangeManager.subscribe(this) launchCatchingIO { @@ -120,6 +128,7 @@ class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) : while (!statesMutated) { delay(50) } + updateNextTimes() showAnswerInternal() loadAndPlaySounds(CardSide.ANSWER) } @@ -372,6 +381,14 @@ class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) : redoLabelFlow.emit(withCol { redoLabel() }) } + private suspend fun updateNextTimes() { + if (!shouldShowNextTimes.await()) return + val state = queueState.await() ?: return + + val nextTimes = AnswerButtonsNextTime.from(state) + answerButtonsNextTimeFlow.emit(nextTimes) + } + override fun opExecuted(changes: OpChanges, handler: Any?) { launchCatchingIO { updateUndoAndRedoLabels() } } @@ -384,5 +401,19 @@ class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) : } } } + + fun buildAnswerButtonText(title: String, nextTime: String?): CharSequence { + return if (nextTime != null) { + buildSpannedString { + inSpans(RelativeSizeSpan(0.8F)) { + append(nextTime) + } + append("\n") + append(title) + } + } else { + title + } + } } }