diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt index f15b3dfd6536..aad2eb588c7f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt @@ -68,6 +68,7 @@ import com.ichi2.anki.dialogs.tags.TagsDialog import com.ichi2.anki.dialogs.tags.TagsDialogFactory import com.ichi2.anki.dialogs.tags.TagsDialogListener import com.ichi2.anki.model.CardStateFilter +import com.ichi2.anki.pages.CongratsPage import com.ichi2.anki.preferences.sharedPrefs import com.ichi2.anki.receiver.SdCardReceiver import com.ichi2.anki.reviewer.* @@ -508,7 +509,7 @@ abstract class AbstractFlashcardViewer : closeReviewer(RESULT_NO_MORE_CARDS) // When launched with a shortcut, we want to display a message when finishing if (intent.getBooleanExtra(EXTRA_STARTED_WITH_SHORTCUT, false)) { - showThemedToast(baseContext, R.string.studyoptions_congrats_finished, false) + startActivity(CongratsPage.getIntent(this)) } return } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt index b99f55b9dfaa..8e1c64195ebc 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt @@ -83,13 +83,13 @@ import com.ichi2.anki.dialogs.ImportFileSelectionFragment.CsvImportResultLaunche import com.ichi2.anki.dialogs.MediaCheckDialog.MediaCheckDialogListener import com.ichi2.anki.dialogs.SyncErrorDialog.Companion.newInstance import com.ichi2.anki.dialogs.SyncErrorDialog.SyncErrorDialogListener -import com.ichi2.anki.dialogs.customstudy.CustomStudyDialog import com.ichi2.anki.dialogs.customstudy.CustomStudyDialog.CustomStudyListener import com.ichi2.anki.dialogs.customstudy.CustomStudyDialogFactory import com.ichi2.anki.export.ActivityExportingDelegate import com.ichi2.anki.export.ExportType import com.ichi2.anki.notetype.ManageNotetypes import com.ichi2.anki.pages.AnkiPackageImporterFragment +import com.ichi2.anki.pages.CongratsPage import com.ichi2.anki.preferences.AdvancedSettingsFragment import com.ichi2.anki.preferences.sharedPrefs import com.ichi2.anki.receiver.SdCardReceiver @@ -966,12 +966,7 @@ open class DeckPicker : private fun processReviewResults(resultCode: Int) { if (resultCode == AbstractFlashcardViewer.RESULT_NO_MORE_CARDS) { - // Show a message when reviewing has finished - if (getColUnsafe.sched.totalCount() == 0) { - showSnackbar(R.string.studyoptions_congrats_finished) - } else { - showSnackbar(R.string.studyoptions_no_cards_due) - } + startActivity(CongratsPage.getIntent(this)) } else if (resultCode == AbstractFlashcardViewer.RESULT_ABORT_AND_SYNC) { Timber.i("Obtained Abort and Sync result") sync() @@ -1720,36 +1715,11 @@ open class DeckPicker : @NeedsTest("14608: Ensure that the deck options refer to the selected deck") private suspend fun handleDeckSelection(did: DeckId, selectionType: DeckSelectionType) { - fun showStudyMoreSnackbar(did: DeckId) = - showSnackbar(R.string.studyoptions_limit_reached) { - addCallback(mSnackbarShowHideCallback) - setAction(R.string.study_more) { - val d = mCustomStudyDialogFactory.newCustomStudyDialog().withArguments( - CustomStudyDialog.ContextMenuConfiguration.LIMITS, - did, - true - ) - showDialogFragment(d) - } - } - fun showEmptyDeckSnackbar() = showSnackbar(R.string.empty_deck) { addCallback(mSnackbarShowHideCallback) setAction(R.string.menu_add) { addNote() } } - fun showCustomStudySnackbar() = showSnackbar(R.string.studyoptions_empty_schedule) { - addCallback(mSnackbarShowHideCallback) - setAction(R.string.custom_study) { - val d = mCustomStudyDialogFactory.newCustomStudyDialog().withArguments( - CustomStudyDialog.ContextMenuConfiguration.EMPTY_SCHEDULE, - did, - true - ) - showDialogFragment(d) - } - } - /** Check if we need to update the fragment or update the deck list */ fun updateUi() { if (fragmented) { @@ -1779,29 +1749,17 @@ open class DeckPicker : } when (queryCompletedDeckCustomStudyAction(did)) { - CompletedDeckStatus.LEARN_AHEAD_LIMIT_REACHED -> { - // If there are cards due that can't be studied yet (due to the learn ahead limit) then go to study options - openStudyOptions(withDeckOptions = false) - } + CompletedDeckStatus.LEARN_AHEAD_LIMIT_REACHED, + CompletedDeckStatus.REGULAR_DECK_NO_MORE_CARDS_TODAY, + CompletedDeckStatus.DYNAMIC_DECK_NO_LIMITS_REACHED, CompletedDeckStatus.DAILY_STUDY_LIMIT_REACHED -> { - // If there are no cards to review because of the daily study limit then give "Study more" option - showStudyMoreSnackbar(did) - updateUi() - } - CompletedDeckStatus.DYNAMIC_DECK_NO_LIMITS_REACHED -> { - // Go to the study options screen if filtered deck with no cards to study - openStudyOptions(withDeckOptions = false) + startActivity(CongratsPage.getIntent(this)) } CompletedDeckStatus.EMPTY_REGULAR_DECK -> { // If the deck is empty (& has no children) then show a message saying it's empty showEmptyDeckSnackbar() updateUi() } - CompletedDeckStatus.REGULAR_DECK_NO_MORE_CARDS_TODAY -> { - // Otherwise say there are no cards scheduled to study, and give option to do custom study - showCustomStudySnackbar() - updateUi() - } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiServer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiServer.kt index 7893e76f385b..72a217e881e5 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiServer.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/AnkiServer.kt @@ -93,6 +93,7 @@ open class AnkiServer( "setWantsAbort" -> CollectionManager.getBackend().setWantsAbortRaw(bytes) "evaluateWeights" -> withCol { evaluateWeightsRaw(bytes) } "latestProgress" -> CollectionManager.getBackend().latestProgressRaw(bytes) + "congratsInfo" -> withCol { congratsInfoRaw(bytes) } else -> { throw Exception("unhandled request: $methodName") } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/CongratsPage.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/CongratsPage.kt new file mode 100644 index 000000000000..10835b6e5043 --- /dev/null +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/CongratsPage.kt @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2023 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.pages + +import android.content.Context +import android.content.Intent + +class CongratsPage : PageFragment() { + override val title: Int? = null + override val pageName = "congrats" + override var webViewClient: PageWebViewClient = PageWebViewClient() + override var webChromeClient: PageChromeClient = PageChromeClient() + + companion object { + fun getIntent(context: Context): Intent { + return PagesActivity.getIntent(context, CongratsPage::class) + } + } +} diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.kt index 13e47aa477ba..a20e703c9e2e 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.kt +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.kt @@ -27,6 +27,7 @@ import anki.card_rendering.EmptyCardsReport import anki.collection.OpChanges import anki.collection.OpChangesWithCount import anki.config.ConfigKey +import anki.scheduler.CongratsInfoResponse import anki.search.SearchNode import anki.sync.SyncAuth import anki.sync.SyncStatusResponse @@ -715,4 +716,16 @@ open class Collection( return backend.clozeNumbersInNote(n.toBackendNote()) .sorted() } + + // TODO either support bridgeCommand here + // or replace it with POST requests in Anki Desktop (preferable) + // https://github.com/ankidroid/Anki-Android/issues/14361#issuecomment-1701946364 + fun congratsInfoRaw(input: ByteArray): ByteArray { + val byteArray = backend.congratsInfoRaw(input = input) + val response = CongratsInfoResponse.parseFrom(byteArray) + return CongratsInfoResponse.newBuilder(response) + .setBridgeCommandsSupported(false) + .build() + .toByteArray() + } } diff --git a/AnkiDroid/src/main/res/values/01-core.xml b/AnkiDroid/src/main/res/values/01-core.xml index 86fbbd03d7a5..6d5c9421fe1c 100644 --- a/AnkiDroid/src/main/res/values/01-core.xml +++ b/AnkiDroid/src/main/res/values/01-core.xml @@ -36,7 +36,6 @@ Expand Collapse - Study more %d minute left %d minutes left @@ -119,9 +118,6 @@ Deck Search Invalid deck name This deck is empty. Press the + button to add new content. - Daily study limit reached - No cards scheduled to study - Congratulations! You have finished for now. No cards are due yet Device storage not mounted diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/AbstractFlashcardViewerTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/AbstractFlashcardViewerTest.kt index 2919060d06ad..8c3063413f9e 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/AbstractFlashcardViewerTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/AbstractFlashcardViewerTest.kt @@ -45,7 +45,6 @@ import org.mockito.Mockito.* import org.robolectric.Robolectric import org.robolectric.Shadows import org.robolectric.android.controller.ActivityController -import org.robolectric.shadows.ShadowToast import java.util.* import java.util.stream.Stream import com.ichi2.anim.ActivityTransitionAnimation.Direction as Direction @@ -252,14 +251,6 @@ class AbstractFlashcardViewerTest : RobolectricTest() { assertThat("no auto answer after onRenderProcessGone when paused", viewer.hasAutomaticAnswerQueued(), equalTo(false)) } - @Test - fun shortcutShowsToastOnFinish() = runTest { - val viewer: NonAbstractFlashcardViewer = getViewer(true, true) - viewer.executeCommand(ViewerCommand.FLIP_OR_ANSWER_EASE4) - viewer.executeCommand(ViewerCommand.FLIP_OR_ANSWER_EASE4) - assertEquals(getResourceString(R.string.studyoptions_congrats_finished), ShadowToast.getTextOfLatestToast()) - } - @Test fun `Show audio play buttons preference handling - sound`() = runTest { addNoteUsingBasicTypedModel("SOUND [sound:android_audiorec.3gp]", "back")