diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/NoteEditorIntentTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/NoteEditorIntentTest.kt index 43cf6c94da..005614e5a3 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/NoteEditorIntentTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/NoteEditorIntentTest.kt @@ -16,11 +16,12 @@ package com.ichi2.anki import android.content.Intent -import android.os.Bundle +import androidx.core.os.bundleOf import androidx.lifecycle.Lifecycle import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.ext.junit.runners.AndroidJUnit4 import com.ichi2.anki.NoteEditor.Companion.intentLaunchedWithImage +import com.ichi2.anki.noteeditor.NoteEditorLauncher import com.ichi2.anki.tests.InstrumentedTest import com.ichi2.anki.testutil.GrantStoragePermission import com.ichi2.anki.testutil.getEditor @@ -71,11 +72,7 @@ class NoteEditorIntentTest : InstrumentedTest() { private val noteEditorTextIntent: Intent get() { - val bundle = Bundle().apply { - putString(Intent.EXTRA_TEXT, "sample text") - } - val intent = NoteEditor.getIntent(testContext, bundle) - intent.action = Intent.ACTION_SEND - return intent + val bundle = bundleOf(Intent.EXTRA_TEXT to "sample text") + return NoteEditorLauncher.PassArguments(bundle).getIntent(testContext, Intent.ACTION_SEND) } } diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/NoteEditorTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/NoteEditorTest.kt index dbdf297e53..27c623bbca 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/NoteEditorTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/NoteEditorTest.kt @@ -18,9 +18,9 @@ package com.ichi2.anki import android.content.Context import android.content.Intent import android.os.Build -import android.os.Bundle import androidx.test.ext.junit.rules.ActivityScenarioRule import androidx.test.platform.app.InstrumentationRegistry +import com.ichi2.anki.noteeditor.NoteEditorLauncher import com.ichi2.anki.testutil.GrantStoragePermission import com.ichi2.utils.KotlinCleanup import org.hamcrest.Matchers.equalTo @@ -41,10 +41,7 @@ abstract class NoteEditorTest protected constructor() { private val noteEditorIntent: Intent get() { - val bundle = Bundle().apply { - putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_DECKPICKER) - } - return NoteEditor.getIntent(targetContext, bundle) + return NoteEditorLauncher.AddNote().getIntent(targetContext) } @Before diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt index cc08f367b7..7beadd222e 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AbstractFlashcardViewer.kt @@ -105,8 +105,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.noteeditor.EditCardDestination -import com.ichi2.anki.noteeditor.toIntent +import com.ichi2.anki.noteeditor.NoteEditorLauncher import com.ichi2.anki.pages.AnkiServer import com.ichi2.anki.pages.CongratsPage import com.ichi2.anki.pages.PostRequestHandler @@ -847,7 +846,7 @@ abstract class AbstractFlashcardViewer : return } val animation = fromGesture.toAnimationTransition().invert() - val editCardIntent = EditCardDestination(currentCard!!.id).toIntent(this, animation) + val editCardIntent = NoteEditorLauncher.EditCard(currentCard!!.id, animation).getIntent(this) editCurrentCardLauncher.launch(editCardIntent) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt index dc67224284..74e358080f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt @@ -95,8 +95,7 @@ import com.ichi2.anki.model.CardsOrNotes import com.ichi2.anki.model.CardsOrNotes.CARDS import com.ichi2.anki.model.CardsOrNotes.NOTES import com.ichi2.anki.model.SortType -import com.ichi2.anki.noteeditor.EditCardDestination -import com.ichi2.anki.noteeditor.toIntent +import com.ichi2.anki.noteeditor.NoteEditorLauncher import com.ichi2.anki.preferences.sharedPrefs import com.ichi2.anki.previewer.PreviewerFragment import com.ichi2.anki.receiver.SdCardReceiver @@ -713,7 +712,7 @@ open class CardBrowser : @NeedsTest("I/O edits are saved") private fun openNoteEditorForCard(cardId: CardId) { currentCardId = cardId - val intent = EditCardDestination(currentCardId).toIntent(this, animation = Direction.DEFAULT) + val intent = NoteEditorLauncher.EditCard(currentCardId, Direction.DEFAULT).getIntent(this) onEditCardActivityResult.launch(intent) // #6432 - FIXME - onCreateOptionsMenu crashes if receiving an activity result from edit card when in multiselect viewModel.endMultiSelectMode() @@ -2222,14 +2221,7 @@ open class CardBrowser : @VisibleForTesting fun createAddNoteIntent(context: Context, viewModel: CardBrowserViewModel): Intent { - val bundle = Bundle().apply { - putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_CARDBROWSER_ADD) - if (viewModel.lastDeckId?.let { id -> id > 0 } == true) { - putLong(NoteEditor.EXTRA_DID, viewModel.lastDeckId!!) - } - putString(NoteEditor.EXTRA_TEXT_FROM_SEARCH_VIEW, viewModel.searchTerms) - } - return NoteEditor.getIntent(context, bundle) + return NoteEditorLauncher.AddNoteFromCardBrowser(viewModel).getIntent(context) } @CheckResult diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt index f0acbb89a6..454e3d4182 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt @@ -135,6 +135,7 @@ import com.ichi2.anki.export.ExportDialogsFactory import com.ichi2.anki.export.ExportDialogsFactoryProvider import com.ichi2.anki.introduction.CollectionPermissionScreenLauncher import com.ichi2.anki.introduction.hasCollectionStoragePermissions +import com.ichi2.anki.noteeditor.NoteEditorLauncher import com.ichi2.anki.notetype.ManageNotetypes import com.ichi2.anki.pages.AnkiPackageImporterFragment import com.ichi2.anki.pages.CongratsPage @@ -1511,10 +1512,7 @@ open class DeckPicker : } fun addNote() { - val bundle = Bundle().apply { - putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_DECKPICKER) - } - val intent = NoteEditor.getIntent(this, bundle) + val intent = NoteEditorLauncher.AddNote().getIntent(this) startActivity(intent) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/ImageOcclusionIntentBuilder.kt b/AnkiDroid/src/main/java/com/ichi2/anki/ImageOcclusionIntentBuilder.kt index 39a7992780..e202b67de4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/ImageOcclusionIntentBuilder.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/ImageOcclusionIntentBuilder.kt @@ -19,17 +19,13 @@ package com.ichi2.anki import android.content.Context import android.content.Intent import android.net.Uri -import android.os.Bundle +import com.ichi2.anki.noteeditor.NoteEditorLauncher /** * Builder class for creating intents related to image occlusion in the [NoteEditor]. */ class ImageOcclusionIntentBuilder(private val context: Context) { fun buildIntent(imageUri: Uri?): Intent { - val bundle = Bundle().apply { - putParcelable(NoteEditor.EXTRA_IMG_OCCLUSION, imageUri) - putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_IMG_OCCLUSION) - } - return NoteEditor.getIntent(context, bundle) + return NoteEditorLauncher.ImageOcclusion(imageUri).getIntent(context) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler2.kt b/AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler2.kt index bb6ba97280..963206b1b3 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler2.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/IntentHandler2.kt @@ -17,6 +17,7 @@ package com.ichi2.anki import android.os.Bundle +import com.ichi2.anki.noteeditor.NoteEditorLauncher import timber.log.Timber /** @@ -33,7 +34,7 @@ class IntentHandler2 : AbstractIntentHandler() { Timber.i("Intent contained an image") intent.putExtra(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_ADD_IMAGE) } - val noteEditorIntent = NoteEditor.getIntent(this, intent.extras!!, intent.action) + val noteEditorIntent = NoteEditorLauncher.PassArguments(intent.extras!!).getIntent(this, intent.action) noteEditorIntent.setDataAndType(intent.data, intent.type) startActivity(noteEditorIntent) finish() diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.kt index 1d0037b45d..8e5b8a0c0e 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.kt @@ -100,6 +100,7 @@ import com.ichi2.anki.multimediacard.impl.MultimediaEditableNote import com.ichi2.anki.noteeditor.CustomToolbarButton import com.ichi2.anki.noteeditor.FieldState import com.ichi2.anki.noteeditor.FieldState.FieldChangeType +import com.ichi2.anki.noteeditor.NoteEditorLauncher import com.ichi2.anki.noteeditor.Toolbar.TextFormatListener import com.ichi2.anki.noteeditor.Toolbar.TextWrapper import com.ichi2.anki.pages.ImageOcclusion @@ -1355,25 +1356,17 @@ class NoteEditor : AnkiFragment(R.layout.note_editor), DeckSelectionListener, Su } private fun addNewNote() { - openNewNoteEditor { } + launchNoteEditor(NoteEditorLauncher.AddNote(deckId)) { } } fun copyNote() { - openNewNoteEditor { bundle: Bundle -> - bundle.putString(EXTRA_CONTENTS, fieldsText) - if (selectedTags != null) { - bundle.putStringArray(EXTRA_TAGS, selectedTags!!.toTypedArray()) - } - } + launchNoteEditor(NoteEditorLauncher.CopyNote(deckId, fieldsText, selectedTags)) { } } - private fun openNewNoteEditor(intentEnricher: Consumer) { - val bundle = Bundle().apply { - putInt(EXTRA_CALLER, CALLER_NOTEEDITOR) - putLong(EXTRA_DID, deckId) - } - val intent = getIntent(requireContext(), bundle) - // mutate event with additional properties + private fun launchNoteEditor(arguments: NoteEditorLauncher, intentEnricher: Consumer) { + val intent = arguments.getIntent(requireContext()) + val bundle = arguments.toBundle() + // Mutate event with additional properties intentEnricher.accept(bundle) requestAddLauncher.launch(intent) } @@ -2565,9 +2558,5 @@ class NoteEditor : AnkiFragment(R.layout.note_editor), DeckSelectionListener, Su return !AnkiDroidApp.instance.sharedPrefs() .getBoolean(PREF_NOTE_EDITOR_SHOW_TOOLBAR, true) } - - fun getIntent(context: Context, arguments: Bundle, action: String? = null): Intent { - return SingleFragmentActivity.getIntent(context, NoteEditor::class, arguments, action) - } } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt index 8305705ffc..c7db05ac20 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt @@ -62,6 +62,7 @@ import com.ichi2.anki.Whiteboard.Companion.createInstance import com.ichi2.anki.Whiteboard.OnPaintColorChangeListener import com.ichi2.anki.cardviewer.Gesture import com.ichi2.anki.cardviewer.ViewerCommand +import com.ichi2.anki.noteeditor.NoteEditorLauncher import com.ichi2.anki.pages.AnkiServer.Companion.ANKIDROID_JS_PREFIX import com.ichi2.anki.pages.AnkiServer.Companion.ANKI_PREFIX import com.ichi2.anki.pages.CardInfoDestination @@ -686,11 +687,8 @@ open class Reviewer : fun addNote(fromGesture: Gesture? = null) { val animation = getAnimationTransitionFromGesture(fromGesture) - val bundle = Bundle().apply { - putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_REVIEWER_ADD) - putParcelable(FINISH_ANIMATION_EXTRA, getInverseTransition(animation) as Parcelable) - } - val intent = NoteEditor.getIntent(this, bundle) + val inverseAnimation = getInverseTransition(animation) + val intent = NoteEditorLauncher.AddNoteFromReviewer(inverseAnimation).getIntent(this) addNoteLauncher.launch(intent) } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/instantnoteeditor/InstantNoteEditorActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/instantnoteeditor/InstantNoteEditorActivity.kt index c196c6c990..72a5a82cab 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/instantnoteeditor/InstantNoteEditorActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/instantnoteeditor/InstantNoteEditorActivity.kt @@ -46,11 +46,11 @@ import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CustomActionModeCallback import com.ichi2.anki.DeckSpinnerSelection -import com.ichi2.anki.NoteEditor import com.ichi2.anki.R import com.ichi2.anki.dialogs.DeckSelectionDialog import com.ichi2.anki.dialogs.DiscardChangesDialog import com.ichi2.anki.launchCatchingTask +import com.ichi2.anki.noteeditor.NoteEditorLauncher import com.ichi2.anki.servicelayer.NoteService import com.ichi2.anki.showThemedToast import com.ichi2.anki.withProgress @@ -183,11 +183,7 @@ class InstantNoteEditorActivity : AnkiActivity(), DeckSelectionDialog.DeckSelect private fun openNoteEditor() { val sharedText = clozeEditTextField.text.toString() - val bundle = Bundle().apply { - putInt(NoteEditor.EXTRA_CALLER, NoteEditor.INSTANT_NOTE_EDITOR) - putString(Intent.EXTRA_TEXT, sharedText) - } - val noteEditorIntent = NoteEditor.getIntent(this, bundle) + val noteEditorIntent = NoteEditorLauncher.AddInstantNote(sharedText).getIntent(this) startActivity(noteEditorIntent) finish() } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/EditCardDestination.kt b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/EditCardDestination.kt deleted file mode 100644 index 322d99e2e6..0000000000 --- a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/EditCardDestination.kt +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (c) 2024 David Allison - * - * 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.noteeditor - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.os.Parcelable -import androidx.annotation.CheckResult -import com.ichi2.anim.ActivityTransitionAnimation -import com.ichi2.anki.AnkiActivity -import com.ichi2.anki.NoteEditor -import com.ichi2.libanki.CardId - -/** - * Opens the [Note Editor][NoteEditor] to the note of a selected card - * - * As the card of the note is known, additional context is provided to the UI - */ -data class EditCardDestination(val cardId: CardId) - -@CheckResult -fun EditCardDestination.toIntent(context: Context, animation: ActivityTransitionAnimation.Direction): Intent { - val bundle = Bundle().apply { - putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_EDIT) - putLong(NoteEditor.EXTRA_CARD_ID, cardId) - putParcelable(AnkiActivity.FINISH_ANIMATION_EXTRA, animation as Parcelable) - } - return NoteEditor.getIntent(context, bundle) -} diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/NoteEditorLauncher.kt b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/NoteEditorLauncher.kt new file mode 100644 index 0000000000..e7391b5a30 --- /dev/null +++ b/AnkiDroid/src/main/java/com/ichi2/anki/noteeditor/NoteEditorLauncher.kt @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2024 Sanjay Sargam + * + * 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.noteeditor + +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.os.Parcelable +import androidx.core.os.bundleOf +import com.ichi2.anim.ActivityTransitionAnimation +import com.ichi2.anki.AnkiActivity +import com.ichi2.anki.NoteEditor +import com.ichi2.anki.SingleFragmentActivity +import com.ichi2.anki.browser.CardBrowserViewModel +import com.ichi2.libanki.CardId +import com.ichi2.libanki.DeckId + +/** + * Defines various configurations for opening the NoteEditor fragment with specific data or actions. + */ +sealed interface NoteEditorLauncher { + + /** + * Generates an intent to open the NoteEditor fragment with the configured parameters. + * + * @param context The context from which the intent is launched. + * @param action Optional action string for the intent. + * @return Intent configured to launch the NoteEditor fragment. + */ + fun getIntent(context: Context, action: String? = null) = + SingleFragmentActivity.getIntent(context, NoteEditor::class, toBundle(), action) + + /** + * Converts the configuration into a Bundle to pass arguments to the NoteEditor fragment. + * + * @return Bundle containing arguments specific to this configuration. + */ + fun toBundle(): Bundle + + /** + * Represents opening the NoteEditor with an image occlusion. + * @property imageUri The URI of the image to occlude. + */ + data class ImageOcclusion(val imageUri: Uri?) : NoteEditorLauncher { + override fun toBundle(): Bundle = bundleOf( + NoteEditor.EXTRA_CALLER to NoteEditor.CALLER_IMG_OCCLUSION, + NoteEditor.EXTRA_IMG_OCCLUSION to imageUri + ) + } + + /** + * Represents opening the NoteEditor with custom arguments. + * @property arguments The bundle of arguments to pass. + */ + data class PassArguments(val arguments: Bundle) : NoteEditorLauncher { + override fun toBundle(): Bundle { + return arguments + } + } + + /** + * Represents adding a note to the NoteEditor within a specific deck (Optional). + * @property deckId The ID of the deck where the note should be added. + */ + data class AddNote(val deckId: DeckId? = null) : NoteEditorLauncher { + override fun toBundle(): Bundle = bundleOf( + NoteEditor.EXTRA_CALLER to NoteEditor.CALLER_DECKPICKER + ).also { bundle -> + deckId?.let { deckId -> bundle.putLong(NoteEditor.EXTRA_DID, deckId) } + } + } + + /** + * Represents adding a note to the NoteEditor from the card browser. + * @property viewModel The view model containing data from the card browser. + */ + data class AddNoteFromCardBrowser(val viewModel: CardBrowserViewModel) : + NoteEditorLauncher { + override fun toBundle(): Bundle { + val bundle = bundleOf( + NoteEditor.EXTRA_CALLER to NoteEditor.CALLER_CARDBROWSER_ADD, + NoteEditor.EXTRA_TEXT_FROM_SEARCH_VIEW to viewModel.searchTerms + ) + if (viewModel.lastDeckId?.let { id -> id > 0 } == true) { + bundle.putLong(NoteEditor.EXTRA_DID, viewModel.lastDeckId!!) + } + return bundle + } + } + + /** + * Represents adding a note to the NoteEditor from the reviewer. + * @property animation The animation direction to use when transitioning. + */ + data class AddNoteFromReviewer(val animation: ActivityTransitionAnimation.Direction? = null) : + NoteEditorLauncher { + override fun toBundle(): Bundle = bundleOf( + NoteEditor.EXTRA_CALLER to NoteEditor.CALLER_REVIEWER_ADD + ).also { bundle -> + animation?.let { animation -> + bundle.putParcelable( + AnkiActivity.FINISH_ANIMATION_EXTRA, + animation as Parcelable + ) + } + } + } + + /** + * Allows to move from Instant note editor to standard note editor while keeping the text content + * + * @property sharedText The shared text content for the instant note. + */ + data class AddInstantNote(val sharedText: String) : NoteEditorLauncher { + override fun toBundle(): Bundle = bundleOf( + NoteEditor.EXTRA_CALLER to NoteEditor.INSTANT_NOTE_EDITOR, + Intent.EXTRA_TEXT to sharedText + ) + } + + /** + * Represents editing a card in the NoteEditor. + * @property cardId The ID of the card to edit. + * @property animation The animation direction. + */ + data class EditCard(val cardId: CardId, val animation: ActivityTransitionAnimation.Direction) : + NoteEditorLauncher { + override fun toBundle(): Bundle = bundleOf( + NoteEditor.EXTRA_CALLER to NoteEditor.CALLER_EDIT, + NoteEditor.EXTRA_CARD_ID to cardId, + AnkiActivity.FINISH_ANIMATION_EXTRA to animation as Parcelable + ) + } + + /** + * Represents editing a note in the NoteEditor from the previewer. + * @property cardId The ID of the card associated with the note to edit. + */ + data class EditNoteFromPreviewer(val cardId: Long) : NoteEditorLauncher { + override fun toBundle(): Bundle = bundleOf( + NoteEditor.EXTRA_CALLER to NoteEditor.CALLER_PREVIEWER_EDIT, + NoteEditor.EXTRA_EDIT_FROM_CARD_ID to cardId + ) + } + + /** + * Represents copying a note to the NoteEditor. + * @property deckId The ID of the deck where the note should be copied. + * @property fieldsText The text content of the fields to copy. + * @property tags Optional list of tags to assign to the copied note. + */ + data class CopyNote( + val deckId: DeckId, + val fieldsText: String, + val tags: List? = null + ) : NoteEditorLauncher { + override fun toBundle(): Bundle = bundleOf( + NoteEditor.EXTRA_CALLER to NoteEditor.CALLER_NOTEEDITOR, + NoteEditor.EXTRA_DID to deckId, + NoteEditor.EXTRA_CONTENTS to fieldsText + ).also { bundle -> + tags?.let { tags -> bundle.putStringArray(NoteEditor.EXTRA_TAGS, tags.toTypedArray()) } + } + } +} diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerFragment.kt index 8f8826419e..0a6744fe53 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerFragment.kt @@ -234,7 +234,7 @@ class PreviewerFragment : private fun editCard() { lifecycleScope.launch { - val intent = viewModel.getNoteEditorDestination().toIntent(requireContext()) + val intent = viewModel.getNoteEditorDestination().getIntent(requireContext()) editCardLauncher.launch(intent) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerHelpers.kt b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerHelpers.kt index 3b426e1fc0..3bdc56dea7 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerHelpers.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerHelpers.kt @@ -17,24 +17,13 @@ package com.ichi2.anki.previewer import android.R import android.content.Context -import android.content.Intent -import android.os.Bundle import com.google.android.material.color.MaterialColors import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.LanguageUtils -import com.ichi2.anki.NoteEditor import com.ichi2.themes.Themes import com.ichi2.utils.toRGBHex import org.intellij.lang.annotations.Language -class NoteEditorDestination(val cardId: Long) { - val bundle = Bundle().apply { - putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_PREVIEWER_EDIT) - putLong(NoteEditor.EXTRA_EDIT_FROM_CARD_ID, cardId) - } - fun toIntent(context: Context): Intent = NoteEditor.getIntent(context, bundle) -} - /** * Not exactly equal to anki's stdHtml. Some differences: * * `ankidroid.css` and `ankidroid.js` are added diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerViewModel.kt b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerViewModel.kt index 565c6354e6..96a4c4475c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerViewModel.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/previewer/PreviewerViewModel.kt @@ -28,6 +28,7 @@ import com.ichi2.anki.browser.PreviewerIdsFile import com.ichi2.anki.cardviewer.CardMediaPlayer import com.ichi2.anki.cardviewer.SingleCardSide import com.ichi2.anki.launchCatchingIO +import com.ichi2.anki.noteeditor.NoteEditorLauncher import com.ichi2.anki.pages.AnkiServer import com.ichi2.anki.reviewer.CardSide import com.ichi2.anki.servicelayer.MARKED_TAG @@ -168,7 +169,7 @@ class PreviewerViewModel(previewerIdsFile: PreviewerIdsFile, firstIndex: Int, ca } } - suspend fun getNoteEditorDestination() = NoteEditorDestination(currentCard.await().id) + suspend fun getNoteEditorDestination() = NoteEditorLauncher.EditNoteFromPreviewer(currentCard.await().id) fun handleEditCardResult(result: ActivityResult) { if (result.data?.getBooleanExtra(NoteEditor.RELOAD_REQUIRED_EXTRA_KEY, false) == true || 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 51881d8236..3bc80b0175 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 @@ -355,7 +355,7 @@ class ReviewerFragment : private fun launchEditNote() { lifecycleScope.launch { - val intent = viewModel.getEditNoteDestination().toIntent(requireContext()) + val intent = viewModel.getEditNoteDestination().getIntent(requireContext()) noteEditorLauncher.launch(intent) } } 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 1a616b3e1d..2264a23b6c 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 @@ -31,11 +31,11 @@ import com.ichi2.anki.Reviewer import com.ichi2.anki.asyncIO import com.ichi2.anki.cardviewer.CardMediaPlayer import com.ichi2.anki.launchCatchingIO +import com.ichi2.anki.noteeditor.NoteEditorLauncher import com.ichi2.anki.pages.AnkiServer import com.ichi2.anki.pages.CardInfoDestination import com.ichi2.anki.pages.DeckOptionsDestination import com.ichi2.anki.previewer.CardViewerViewModel -import com.ichi2.anki.previewer.NoteEditorDestination import com.ichi2.anki.reviewer.CardSide import com.ichi2.anki.servicelayer.MARKED_TAG import com.ichi2.anki.servicelayer.NoteService @@ -181,8 +181,8 @@ class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) : statesMutated = true } - suspend fun getEditNoteDestination(): NoteEditorDestination { - return NoteEditorDestination(currentCard.await().id) + suspend fun getEditNoteDestination(): NoteEditorLauncher { + return NoteEditorLauncher.EditNoteFromPreviewer(currentCard.await().id) } fun refreshCard() { diff --git a/AnkiDroid/src/main/java/com/ichi2/widget/AddNoteWidget.kt b/AnkiDroid/src/main/java/com/ichi2/widget/AddNoteWidget.kt index 33d709a3a0..cb6d4d2908 100644 --- a/AnkiDroid/src/main/java/com/ichi2/widget/AddNoteWidget.kt +++ b/AnkiDroid/src/main/java/com/ichi2/widget/AddNoteWidget.kt @@ -17,13 +17,12 @@ package com.ichi2.widget import android.appwidget.AppWidgetManager import android.appwidget.AppWidgetProvider import android.content.Context -import android.os.Bundle import android.widget.RemoteViews import androidx.core.app.PendingIntentCompat import com.ichi2.anki.IntentHandler -import com.ichi2.anki.NoteEditor import com.ichi2.anki.R import com.ichi2.anki.analytics.UsageAnalytics +import com.ichi2.anki.noteeditor.NoteEditorLauncher import timber.log.Timber class AddNoteWidget : AppWidgetProvider() { @@ -63,10 +62,7 @@ class AddNoteWidget : AppWidgetProvider() { appWidgetIds: IntArray ) { val remoteViews = RemoteViews(context.packageName, R.layout.widget_add_note) - val bundle = Bundle().apply { - putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_DECKPICKER) - } - val intent = NoteEditor.getIntent(context, bundle) + val intent = NoteEditorLauncher.AddNote().getIntent(context) val pendingIntent = PendingIntentCompat.getActivity(context, 0, intent, 0, false) remoteViews.setOnClickPendingIntent(R.id.widget_add_note_button, pendingIntent) appWidgetManager.updateAppWidget(appWidgetIds, remoteViews) diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/AbstractFlashcardViewerTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/AbstractFlashcardViewerTest.kt index 732569e28a..eaf38c266d 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/AbstractFlashcardViewerTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/AbstractFlashcardViewerTest.kt @@ -4,11 +4,11 @@ package com.ichi2.anki import android.content.Intent import android.os.Build -import android.os.Bundle import android.os.Parcelable import android.webkit.RenderProcessGoneDetail import androidx.annotation.CheckResult import androidx.core.os.BundleCompat +import androidx.core.os.bundleOf import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SdkSuppress import anki.config.ConfigKey @@ -204,11 +204,11 @@ class AbstractFlashcardViewerTest : RobolectricTest() { ActivityTransitionAnimation.getInverseTransition(expectedAnimation) val animation = gesture.toAnimationTransition().invert() - val bundle = Bundle().apply { - putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_EDIT) - putLong(NoteEditor.EXTRA_CARD_ID, viewer.currentCard!!.id) - putParcelable(FINISH_ANIMATION_EXTRA, animation as Parcelable) - } + val bundle = bundleOf( + NoteEditor.EXTRA_CALLER to NoteEditor.CALLER_EDIT, + NoteEditor.EXTRA_CARD_ID to viewer.currentCard!!.id, + FINISH_ANIMATION_EXTRA to animation as Parcelable + ) val noteEditor = NoteEditorTest().openNoteEditorWithArgs(bundle) val actualInverseAnimation = BundleCompat.getParcelable( noteEditor.requireArguments(), diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorTest.kt index 9559eb9c18..4ff50b9f1d 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/NoteEditorTest.kt @@ -21,7 +21,6 @@ import android.app.Activity import android.content.ClipData import android.content.Intent import android.os.Bundle -import android.os.Parcelable import android.widget.EditText import android.widget.Spinner import android.widget.TextView @@ -34,6 +33,7 @@ import com.ichi2.anki.NoteEditorTest.FromScreen.REVIEWER import com.ichi2.anki.api.AddContentApi.Companion.DEFAULT_DECK_ID import com.ichi2.anki.dialogs.DeckSelectionDialog.SelectableDeck import com.ichi2.anki.multimediacard.activity.MultimediaEditFieldActivity +import com.ichi2.anki.noteeditor.NoteEditorLauncher import com.ichi2.anki.utils.ext.isImageOcclusion import com.ichi2.annotations.DuplicatedCode import com.ichi2.libanki.Consts @@ -199,10 +199,7 @@ class NoteEditorTest : RobolectricTest() { @Test fun verifyStartupAndCloseWithNoCollectionDoesNotCrash() { enableNullCollection() - val bundle = Bundle().apply { - putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_NO_CALLER) - } - val intent = NoteEditor.getIntent(targetContext, bundle) + val intent = NoteEditorLauncher.AddNote().getIntent(targetContext) ActivityScenario.launchActivityForResult(intent).use { scenario -> scenario.onNoteEditor { noteEditor -> noteEditor.requireActivity().onBackPressedDispatcher.onBackPressed() @@ -519,12 +516,11 @@ class NoteEditorTest : RobolectricTest() { private fun getNoteEditorAddingNote(from: FromScreen): NoteEditor { ensureCollectionLoadIsSynchronous() - val bundle = Bundle().apply { + val bundle = when (from) { - REVIEWER -> putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_REVIEWER_ADD) - DECK_LIST -> putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_DECKPICKER) + REVIEWER -> NoteEditorLauncher.AddNoteFromReviewer().toBundle() + DECK_LIST -> NoteEditorLauncher.AddNote().toBundle() } - } return openNoteEditorWithArgs(bundle) } @@ -534,22 +530,16 @@ class NoteEditorTest : RobolectricTest() { } private fun getNoteEditorEditingExistingBasicNote(n: Note, from: FromScreen): NoteEditor { - val bundle = Bundle() - when (from) { - REVIEWER -> { - bundle.putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_EDIT) - bundle.putLong(NoteEditor.EXTRA_CARD_ID, n.firstCard().id) - bundle.putParcelable(AnkiActivity.FINISH_ANIMATION_EXTRA, DEFAULT as Parcelable) + val bundle = + when (from) { + REVIEWER -> NoteEditorLauncher.EditCard(n.firstCard().id, DEFAULT).toBundle() + DECK_LIST -> NoteEditorLauncher.AddNote().toBundle() } - DECK_LIST -> { - bundle.putInt(NoteEditor.EXTRA_CALLER, NoteEditor.CALLER_DECKPICKER) - } - } return openNoteEditorWithArgs(bundle) } fun openNoteEditorWithArgs(arguments: Bundle, action: String? = null): NoteEditor { - val activity = startActivityNormallyOpenCollectionWithIntent(SingleFragmentActivity::class.java, NoteEditor.getIntent(targetContext, arguments, action)) + val activity = startActivityNormallyOpenCollectionWithIntent(SingleFragmentActivity::class.java, NoteEditorLauncher.PassArguments(arguments).getIntent(targetContext, action)) return activity.getEditor() }