diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/BrowserMultiColumnAdapter.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/BrowserMultiColumnAdapter.kt index 059695795b66..498ee78b9b27 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/BrowserMultiColumnAdapter.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/BrowserMultiColumnAdapter.kt @@ -207,8 +207,15 @@ class BrowserMultiColumnAdapter( try { val (row, isSelected) = viewModel.transformBrowserRow(id) - holder.firstColumn = row.getCells(0).text - holder.secondColumn = row.getCells(1).text + + // PERF: removeSounds only needs to be performed on QUESTION/ANSWER columns + fun renderColumn(columnIndex: Int): String = + removeSounds( + input = row.getCells(columnIndex).text, + showMediaFilenames = viewModel.showMediaFilenames, + ) + holder.firstColumn = renderColumn(0) + holder.secondColumn = renderColumn(1) holder.setIsSelected(isSelected) holder.setColor(backendColorToColor(row.color)) holder.setIsDeleted(false) @@ -246,6 +253,20 @@ class BrowserMultiColumnAdapter( } companion object { + private val mediaFilenameRegex = Regex("\uD83D\uDD09(.*?)\uD83D\uDD09") // ๐Ÿ”‰(.*?)๐Ÿ”‰ + + /** + * Strips instances of '๐Ÿ”‰filename.mp3๐Ÿ”‰' if [showMediaFilenames] is not set + */ + @VisibleForTesting + fun removeSounds( + input: String, + showMediaFilenames: Boolean, + ): String { + if (showMediaFilenames) return input + return mediaFilenameRegex.replace(input, "") + } + private const val DEFAULT_FONT_SIZE_RATIO = 100 @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserViewModel.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserViewModel.kt index 8de5557286e4..443fe3ae11c1 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserViewModel.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserViewModel.kt @@ -96,6 +96,9 @@ class CardBrowserViewModel( private val manualInit: Boolean = false, ) : ViewModel(), SharedPreferencesProvider by preferences { + // TODO: abstract so we can use a `Context` and `pref_display_filenames_in_browser_key` + val showMediaFilenames = sharedPrefs().getBoolean("card_browser_show_media_filenames", false) + /** A job which ensures that parallel searches do not occur */ var searchJob: Job? = null private set @@ -111,9 +114,8 @@ class CardBrowserViewModel( private set private var restrictOnDeck: String = "" - // flowOfFilterQuery does not currently bind to the value in the UI and is only used for posting - /** text in the search box (potentially unsubmitted) */ + // this does not currently bind to the value in the UI and is only used for posting val flowOfFilterQuery = MutableSharedFlow() /** diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/CardBrowserTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/CardBrowserTest.kt index 5a75dc3871e9..19e94f1a06bc 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/CardBrowserTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/CardBrowserTest.kt @@ -42,6 +42,7 @@ import com.ichi2.anki.browser.CardBrowserColumn.QUESTION import com.ichi2.anki.browser.CardBrowserColumn.SFLD import com.ichi2.anki.browser.CardBrowserColumn.TAGS import com.ichi2.anki.browser.CardBrowserViewModel +import com.ichi2.anki.browser.CardBrowserViewModelTest import com.ichi2.anki.browser.CardOrNoteId import com.ichi2.anki.common.utils.isRunningAsUnitTest import com.ichi2.anki.dialogs.DeckSelectionDialog @@ -1123,7 +1124,7 @@ class CardBrowserTest : RobolectricTest() { } val question = card.getColumnHeaderText(QUESTION) - assertThat(question, equalTo("\uD83D\uDCACTest\uD83D\uDCAC")) + assertThat(question, equalTo(CardBrowserViewModelTest.EXPECTED_TTS)) } @Test diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/browser/BrowserMultiColumnAdapterTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/browser/BrowserMultiColumnAdapterTest.kt new file mode 100644 index 000000000000..6dcbdc370e2b --- /dev/null +++ b/AnkiDroid/src/test/java/com/ichi2/anki/browser/BrowserMultiColumnAdapterTest.kt @@ -0,0 +1,56 @@ +/* + * 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.browser + +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.equalTo +import org.junit.Test + +/** + * Tests for [BrowserMultiColumnAdapter] + */ +class BrowserMultiColumnAdapterTest { + companion object { + const val EXPECTED_SOUND = "\uD83D\uDD09david.mp3\uD83D\uDD09" + const val TTS = "\uD83D\uDCACTest\uD83D\uDCAC" + } + + @Test + fun `sound without filenames`() { + val text = BrowserMultiColumnAdapter.removeSounds(EXPECTED_SOUND, showMediaFilenames = false) + assertThat("sound filename stripped", text, equalTo("")) + } + + @Test + fun `tts not affected`() { + val text = BrowserMultiColumnAdapter.removeSounds(TTS, showMediaFilenames = false) + assertThat("unchanged", text, equalTo(TTS)) + } + + @Test + fun `sound with filenames`() { + val text = BrowserMultiColumnAdapter.removeSounds(EXPECTED_SOUND, showMediaFilenames = true) + assertThat("unchanged", text, equalTo(EXPECTED_SOUND)) + } + + @Test + fun `meta test`() { + // ensure that Anki's output has not changed + assertThat("sound", EXPECTED_SOUND, equalTo(CardBrowserViewModelTest.EXPECTED_SOUND)) + assertThat("tts", TTS, equalTo(CardBrowserViewModelTest.EXPECTED_TTS)) + } +} diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/browser/CardBrowserViewModelTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/browser/CardBrowserViewModelTest.kt index 3201e9281a04..5691f5693a68 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/browser/CardBrowserViewModelTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/browser/CardBrowserViewModelTest.kt @@ -16,6 +16,7 @@ package com.ichi2.anki.browser +import androidx.core.content.edit import androidx.test.ext.junit.runners.AndroidJUnit4 import app.cash.turbine.TurbineTestContext import app.cash.turbine.test @@ -773,6 +774,33 @@ class CardBrowserViewModelTest : JvmTest() { } } + @Test + fun `sound tags regression test`() { + addBasicNote("[sound:david.mp3]") + + showMediaFilenamesPreference = false + + BrowserColumnCollection.update(AnkiDroidApp.sharedPreferencesProvider.sharedPrefs(), CardsOrNotes.CARDS) { + it[0] = QUESTION + return@update true + } + + runViewModelTest { + waitForSearchResults() + val (row, _) = this.transformBrowserRow(this.cards.single()) + val question = row.getCells(0) + assertThat(question.text, equalTo(EXPECTED_SOUND)) + } + } + + private var showMediaFilenamesPreference: Boolean + // hardcoded @string/pref_display_filenames_in_browser_key + get() = AnkiDroidApp.sharedPreferencesProvider.sharedPrefs().getBoolean("card_browser_show_media_filenames", false) + set(value) = + AnkiDroidApp.sharedPreferencesProvider.sharedPrefs().edit { + putBoolean("card_browser_show_media_filenames", value) + } + private fun runViewModelNotesTest( notes: Int = 0, manualInit: Boolean = true, @@ -823,6 +851,9 @@ class CardBrowserViewModelTest : JvmTest() { } companion object { + const val EXPECTED_SOUND = "\uD83D\uDD09david.mp3\uD83D\uDD09" + const val EXPECTED_TTS = "\uD83D\uDCACTest\uD83D\uDCAC" + private suspend fun viewModel( lastDeckId: DeckId? = null, intent: CardBrowserLaunchOptions? = null,