@@ -21,21 +21,36 @@ import android.net.Uri
2121import android.os.Bundle
2222import android.view.View
2323import android.webkit.WebView
24+ import android.widget.Spinner
2425import androidx.activity.addCallback
2526import androidx.core.os.bundleOf
27+ import androidx.fragment.app.viewModels
2628import androidx.lifecycle.lifecycleScope
2729import com.google.android.material.appbar.MaterialToolbar
2830import com.ichi2.anki.CollectionManager.withCol
31+ import com.ichi2.anki.DeckSpinnerSelection
2932import com.ichi2.anki.R
3033import com.ichi2.anki.SingleFragmentActivity
3134import com.ichi2.anki.common.annotations.NeedsTest
35+ import com.ichi2.anki.dialogs.DeckSelectionDialog
3236import com.ichi2.anki.dialogs.DiscardChangesDialog
37+ import com.ichi2.anki.launchCatchingTask
3338import com.ichi2.anki.libanki.DeckId
39+ import com.ichi2.anki.libanki.DeckNameId
40+ import com.ichi2.anki.model.SelectableDeck
41+ import com.ichi2.anki.pages.viewmodel.ImageOcclusionArgs
42+ import com.ichi2.anki.pages.viewmodel.ImageOcclusionViewModel
43+ import com.ichi2.anki.requireAnkiActivity
3444import kotlinx.coroutines.launch
35- import org.json.JSONObject
3645import timber.log.Timber
3746
38- class ImageOcclusion : PageFragment (R .layout.image_occlusion) {
47+ class ImageOcclusion :
48+ PageFragment (R .layout.image_occlusion),
49+ DeckSelectionDialog .DeckSelectionListener {
50+ private val viewModel: ImageOcclusionViewModel by viewModels()
51+ private lateinit var deckSpinnerSelection: DeckSpinnerSelection
52+ private lateinit var spinner: Spinner
53+
3954 override fun onViewCreated (
4055 view : View ,
4156 savedInstanceState : Bundle ? ,
@@ -49,33 +64,34 @@ class ImageOcclusion : PageFragment(R.layout.image_occlusion) {
4964 }
5065 }
5166
67+ spinner = view.findViewById(R .id.deck_selector)
68+ deckSpinnerSelection =
69+ DeckSpinnerSelection (
70+ requireAnkiActivity(),
71+ spinner,
72+ showAllDecks = false ,
73+ alwaysShowDefault = false ,
74+ showFilteredDecks = false ,
75+ )
76+
77+ requireAnkiActivity().launchCatchingTask {
78+ deckSpinnerSelection.initializeStatsBarDeckSpinner()
79+ val selectedDeck = withCol { decks.getLegacy(decks.selected()) }
80+ if (selectedDeck == null ) return @launchCatchingTask
81+ select(selectedDeck.id)
82+ }
83+
5284 @NeedsTest(" #17393 verify that the added image occlusion cards are put in the correct deck" )
5385 view.findViewById<MaterialToolbar >(R .id.toolbar).setOnMenuItemClickListener {
54- val editorWorkingDeckId = requireArguments().getLong(ARG_KEY_EDITOR_DECK_ID )
5586 if (it.itemId == R .id.action_save) {
5687 Timber .i(" save item selected" )
57- // TODO desktop code doesn't allow a deck change from the reviewer, if we would do
58- // the same then NoteEditor could simply set the deck as selected and this hack
59- // could be removed
60- // because NoteEditor doesn't update the selected deck in Collection.decks when
61- // there's a deck change and keeps its own deckId reference, we need to use that
62- // deck id reference as the target deck in this fragment(backend code simply uses
63- // the current selected deck it sees as the target deck for adding)
64- lifecycleScope.launch {
65- val previousDeckId =
66- withCol {
67- val current = backend.getCurrentDeck().id
68- backend.setCurrentDeck(editorWorkingDeckId)
69- current
70- }
71- webView.evaluateJavascript(" anki.imageOcclusion.save()" ) {
72- // reset to the previous deck that the backend "saw" as selected, this
73- // avoids other screens unexpectedly having their working decks modified(
74- // most important being the Reviewer where the user would find itself
75- // studying another deck after editing a note with changing the deck)
76- lifecycleScope.launch {
77- withCol { backend.setCurrentDeck(previousDeckId) }
78- }
88+ webView.evaluateJavascript(" anki.imageOcclusion.save()" ) {
89+ // reset to the previous deck that the backend "saw" as selected, this
90+ // avoids other screens unexpectedly having their working decks modified(
91+ // most important being the Reviewer where the user would find itself
92+ // studying another deck after editing a note with changing the deck)
93+ viewLifecycleOwner.lifecycleScope.launch {
94+ viewModel.onSaveOperationCompleted()
7995 }
8096 }
8197 }
@@ -90,31 +106,45 @@ class ImageOcclusion : PageFragment(R.layout.image_occlusion) {
90106 url : String? ,
91107 ) {
92108 super .onPageFinished(view, url)
93-
94- val kind = requireArguments().getString(ARG_KEY_KIND )
95- val noteOrNotetypeId = requireArguments().getLong(ARG_KEY_ID )
96- val imagePath = requireArguments().getString(ARG_KEY_PATH )
97-
98- val options = JSONObject ()
99- options.put(" kind" , kind)
100- if (kind == " add" ) {
101- options.put(" imagePath" , imagePath)
102- options.put(" notetypeId" , noteOrNotetypeId)
103- } else {
104- options.put(" noteId" , noteOrNotetypeId)
109+ viewModel.webViewOptions.let { options ->
110+ view?.evaluateJavascript(" globalThis.anki.imageOcclusion.mode = $options " ) {
111+ super .onPageFinished(view, url)
112+ }
105113 }
114+ }
115+ }
106116
107- view?.evaluateJavascript(" globalThis.anki.imageOcclusion.mode = $options " ) {
108- super .onPageFinished(view, url)
109- }
117+ override fun onDeckSelected (deck : SelectableDeck ? ) {
118+ if (deck == null ) return
119+ require(deck is SelectableDeck .Deck )
120+
121+ val deckDidChange = viewModel.handleDeckSelection(deck.deckId)
122+ if (deckDidChange) {
123+ viewLifecycleOwner.lifecycleScope.launch {
124+ select(deck.deckId)
125+ deckSpinnerSelection.selectDeckById(viewModel.selectedDeckId, true )
110126 }
111127 }
128+ }
129+
130+ private val decksAdapterSequence
131+ get() =
132+ sequence {
133+ for (i in 0 until spinner.adapter.count) {
134+ yield (spinner.adapter.getItem(i) as DeckNameId )
135+ }
136+ }
137+
138+ /* *
139+ * Given the [deckId] look in the decks adapter for its position and select it if found.
140+ */
141+ private fun select (deckId : DeckId ) {
142+ val itemToSelect = decksAdapterSequence.withIndex().firstOrNull { it.value.id == deckId } ? : return
143+ spinner.setSelection(itemToSelect.index)
144+ }
112145
113146 companion object {
114- private const val ARG_KEY_KIND = " kind"
115- private const val ARG_KEY_ID = " id"
116- private const val ARG_KEY_PATH = " imagePath"
117- private const val ARG_KEY_EDITOR_DECK_ID = " arg_key_editor_deck_id"
147+ const val IO_ARGS_KEY = " IMAGE_OCCLUSION_ARGS"
118148
119149 /* *
120150 * @param editorWorkingDeckId the current deck id that [com.ichi2.anki.NoteEditorFragment] is using
@@ -126,20 +156,22 @@ class ImageOcclusion : PageFragment(R.layout.image_occlusion) {
126156 imagePath : String? ,
127157 editorWorkingDeckId : DeckId ,
128158 ): Intent {
129- val suffix =
130- if (kind == " edit" ) {
131- noteOrNotetypeId
132- } else {
133- Uri .encode(imagePath)
134- }
159+ val suffix = if (kind == " edit" ) noteOrNotetypeId else Uri .encode(imagePath)
160+
161+ val args =
162+ ImageOcclusionArgs (
163+ kind = kind,
164+ id = noteOrNotetypeId,
165+ imagePath = imagePath,
166+ editorDeckId = editorWorkingDeckId,
167+ )
168+
135169 val arguments =
136170 bundleOf(
137- ARG_KEY_KIND to kind,
138- ARG_KEY_ID to noteOrNotetypeId,
139- ARG_KEY_PATH to imagePath,
171+ IO_ARGS_KEY to args,
140172 PATH_ARG_KEY to " image-occlusion/$suffix " ,
141- ARG_KEY_EDITOR_DECK_ID to editorWorkingDeckId,
142173 )
174+
143175 return SingleFragmentActivity .getIntent(context, ImageOcclusion ::class , arguments)
144176 }
145177 }
0 commit comments