Skip to content

Commit

Permalink
refactor(card-browser): extract 'launchOptions'
Browse files Browse the repository at this point in the history
Final extraction from 14843 before testing
  • Loading branch information
david-allison authored and BrayanDSO committed May 22, 2024
1 parent e773164 commit 4f474d1
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 28 deletions.
34 changes: 8 additions & 26 deletions AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,6 @@ open class CardBrowser :

lateinit var viewModel: CardBrowserViewModel

private var launchOptions: CardBrowserLaunchOptions? = null

/** List of cards in the browser.
* When the list is changed, the position member of its elements should get changed. */
private val cards get() = viewModel.cards
Expand Down Expand Up @@ -338,10 +336,10 @@ open class CardBrowser :
if (!ensureStoragePermissions()) {
return
}
val launchOptions = intent?.toCardBrowserLaunchOptions() // must be called after super.onCreate()
// must be called once we have an accessible collection
viewModel = createViewModel()
viewModel = createViewModel(launchOptions)

launchOptions = intent?.toCardBrowserLaunchOptions() // must be called after super.onCreate()
setContentView(R.layout.card_browser)
initNavigationDrawer(findViewById(android.R.id.content))
// initialize the lateinit variables
Expand Down Expand Up @@ -381,18 +379,6 @@ open class CardBrowser :

startLoadingCollection()

when (val options = launchOptions) {
is CardBrowserLaunchOptions.DeepLink -> {
searchCards(options.search)
}
is CardBrowserLaunchOptions.SearchQueryJs -> {
if (options.allDecks) {
onDeckSelected(SelectableDeck(ALL_DECKS_ID, getString(R.string.card_browser_all_decks)))
}
searchCards(options.search)
}
else -> {} // Context Menu handled in onCreateOptionsMenu
}
exportingDelegate.onRestoreInstanceState(savedInstanceState)

// Selected cards aren't restored on activity recreation,
Expand Down Expand Up @@ -803,14 +789,6 @@ open class CardBrowser :
actionBarMenu?.findItem(R.id.action_reschedule_cards)?.title =
TR.actionsSetDueDate().toSentenceCase(R.string.sentence_set_due_date)

launchOptions?.let { options ->
if (options !is CardBrowserLaunchOptions.SystemContextMenu) return@let
// Fill in the search.
Timber.i("CardBrowser :: Called with search intent: %s", launchOptions.toString())
searchWithFilterQuery(options.search.toString())
launchOptions = null
}

previewItem = menu.findItem(R.id.action_preview)
onSelectionChanged()
updatePreviewMenuItem()
Expand Down Expand Up @@ -1899,9 +1877,13 @@ open class CardBrowser :
* @see showedActivityFailedScreen - we may not have AnkiDroidApp.instance and therefore can't
* create the ViewModel
*/
private fun createViewModel() = ViewModelProvider(
private fun createViewModel(launchOptions: CardBrowserLaunchOptions?) = ViewModelProvider(
viewModelStore,
CardBrowserViewModel.factory(AnkiDroidApp.instance.sharedPrefsLastDeckIdRepository, cacheDir),
CardBrowserViewModel.factory(
lastDeckIdRepository = AnkiDroidApp.instance.sharedPrefsLastDeckIdRepository,
cacheDir = cacheDir,
options = launchOptions
),
defaultViewModelCreationExtras
)[CardBrowserViewModel::class.java]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import kotlin.math.min
class CardBrowserViewModel(
private val lastDeckIdRepository: LastDeckIdRepository,
private val cacheDir: File,
options: CardBrowserLaunchOptions?,
preferences: SharedPreferencesProvider
) : ViewModel(), SharedPreferencesProvider by preferences {

Expand Down Expand Up @@ -259,6 +260,18 @@ class CardBrowserViewModel(

init {
Timber.d("CardBrowserViewModel::init")

var selectAllDecks = false
when (options) {
is CardBrowserLaunchOptions.SystemContextMenu -> { searchTerms = options.search.toString() }
is CardBrowserLaunchOptions.SearchQueryJs -> {
searchTerms = options.search
selectAllDecks = options.allDecks
}
is CardBrowserLaunchOptions.DeepLink -> { searchTerms = options.search }
null -> {}
}

flowOfColumnIndex1
.onEach { index -> sharedPrefs().edit { putInt(DISPLAY_COLUMN_1_KEY, index) } }
.launchIn(viewModelScope)
Expand All @@ -282,8 +295,9 @@ class CardBrowserViewModel(
.launchIn(viewModelScope)

viewModelScope.launch {
val initialDeckId = if (selectAllDecks) ALL_DECKS_ID else getInitialDeck()
// PERF: slightly inefficient if the source was lastDeckId
setDeckId(getInitialDeck())
setDeckId(initialDeckId)
val cardsOrNotes = withCol { CardsOrNotes.fromCollection() }
flowOfCardsOrNotes.update { cardsOrNotes }

Expand Down Expand Up @@ -710,11 +724,17 @@ class CardBrowserViewModel(
companion object {
const val DISPLAY_COLUMN_1_KEY = "cardBrowserColumn1"
const val DISPLAY_COLUMN_2_KEY = "cardBrowserColumn2"
fun factory(lastDeckIdRepository: LastDeckIdRepository, cacheDir: File, preferencesProvider: SharedPreferencesProvider? = null) = viewModelFactory {
fun factory(
lastDeckIdRepository: LastDeckIdRepository,
cacheDir: File,
preferencesProvider: SharedPreferencesProvider? = null,
options: CardBrowserLaunchOptions?
) = viewModelFactory {
initializer {
CardBrowserViewModel(
lastDeckIdRepository,
cacheDir,
options,
preferencesProvider ?: AnkiDroidApp.sharedPreferencesProvider
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,23 +19,32 @@ package com.ichi2.anki.browser
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.ichi2.anki.AnkiDroidApp
import com.ichi2.anki.CardBrowser
import com.ichi2.anki.CollectionManager
import com.ichi2.anki.DeckSpinnerSelection
import com.ichi2.anki.Flag
import com.ichi2.anki.NoteEditor
import com.ichi2.anki.browser.CardBrowserLaunchOptions.DeepLink
import com.ichi2.anki.browser.CardBrowserLaunchOptions.SystemContextMenu
import com.ichi2.anki.flagCardForNote
import com.ichi2.anki.model.CardsOrNotes
import com.ichi2.anki.setFlagFilterSync
import com.ichi2.libanki.Consts.QUEUE_TYPE_MANUALLY_BURIED
import com.ichi2.libanki.Consts.QUEUE_TYPE_NEW
import com.ichi2.libanki.DeckId
import com.ichi2.testutils.IntentAssert
import com.ichi2.testutils.JvmTest
import com.ichi2.testutils.createTransientDirectory
import com.ichi2.testutils.mockIt
import kotlinx.coroutines.flow.first
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.nullValue
import org.junit.Test
import org.junit.runner.RunWith
import timber.log.Timber
import java.io.File
import kotlin.io.path.createTempDirectory
import kotlin.io.path.pathString
import kotlin.test.assertNotNull

@RunWith(AndroidJUnit4::class)
Expand Down Expand Up @@ -176,17 +185,61 @@ class CardBrowserViewModelTest : JvmTest() {
assertThat("unbury: queue -> NEW", getQueue(), equalTo(QUEUE_TYPE_NEW))
}

@Test
fun `default init`() = runTest {
viewModel().apply {
assertThat(searchTerms, equalTo(""))
}
}

@Test
fun `Card Browser menu init`() = runTest {
viewModel(intent = SystemContextMenu("Hello")).apply {
assertThat(searchTerms, equalTo("Hello"))
}
}

@Test
fun `Deep Link init`() = runTest {
viewModel(intent = DeepLink("Hello")).apply {
assertThat(searchTerms, equalTo("Hello"))
}
}

private fun runViewModelTest(notes: Int = 0, testBody: suspend CardBrowserViewModel.() -> Unit) = runTest {
for (i in 0 until notes) {
addNoteUsingBasicModel()
}
val viewModel = CardBrowserViewModel(
lastDeckIdRepository = SharedPreferencesLastDeckIdRepository(),
cacheDir = createTransientDirectory(),
options = null,
preferences = AnkiDroidApp.sharedPreferencesProvider
)
testBody(viewModel)
}

companion object {
private suspend fun viewModel(
lastDeckId: DeckId? = null,
intent: CardBrowserLaunchOptions? = null,
mode: CardsOrNotes = CardsOrNotes.CARDS
): CardBrowserViewModel {
val lastDeckIdRepository = object : LastDeckIdRepository {
override var lastDeckId: DeckId? = lastDeckId
}

// default is CARDS, do nothing in this case
if (mode == CardsOrNotes.NOTES) {
CollectionManager.withCol { mode.saveToCollection() }
}

val cache = File(createTempDirectory().pathString)
return CardBrowserViewModel(lastDeckIdRepository, cache, intent, AnkiDroidApp.sharedPreferencesProvider).apply {
invokeInitialSearch()
}
}
}
}

@Suppress("SameParameterValue")
Expand All @@ -201,3 +254,17 @@ private suspend fun CardBrowserViewModel.waitForSearchResults() {
}

private fun CardBrowserViewModel.hasSelectedAllDecks() = lastDeckId == DeckSpinnerSelection.ALL_DECKS_ID

private suspend fun CardBrowserViewModel.waitForInit() {
this.flowOfInitCompleted.first { initCompleted -> initCompleted }
}

internal suspend fun CardBrowserViewModel.invokeInitialSearch() {
Timber.d("waiting for init")
waitForInit()
Timber.d("init completed")
// For legacy reasons, we need to know the number of cards to render when performing a search
// This will be removed once we handle #11889
// numberOfCardsToRenderFlow.emit(1)
Timber.v("initial search completed")
}

0 comments on commit 4f474d1

Please sign in to comment.