Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DRAFT] feat: use backend to render browser columns #41

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidJsAPI.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,14 @@ import com.ichi2.anki.AnkiDroidJsAPIConstants.ankiJsErrorCodeSetDue
import com.ichi2.anki.AnkiDroidJsAPIConstants.ankiJsErrorCodeSuspendCard
import com.ichi2.anki.AnkiDroidJsAPIConstants.ankiJsErrorCodeSuspendNote
import com.ichi2.anki.AnkiDroidJsAPIConstants.flagCommands
import com.ichi2.anki.CollectionManager.withCol
import com.ichi2.anki.cardviewer.ViewerCommand
import com.ichi2.anki.model.CardsOrNotes
import com.ichi2.anki.servicelayer.rescheduleCards
import com.ichi2.anki.servicelayer.resetCards
import com.ichi2.anki.snackbar.setMaxLines
import com.ichi2.anki.snackbar.showSnackbar
import com.ichi2.annotations.NeedsTest
import com.ichi2.libanki.Card
import com.ichi2.libanki.Collection
import com.ichi2.libanki.Decks
Expand Down Expand Up @@ -386,9 +388,11 @@ open class AnkiDroidJsAPI(private val activity: AbstractFlashcardViewer) {
return conversion(apiContract, status)
}

@NeedsTest("needs coverage")
private suspend fun ankiSearchCardWithCallback(apiContract: ApiContract): ByteArray = withContext(Dispatchers.Main) {
val cards = try {
searchForCards(apiContract.cardSuppliedData, SortOrder.UseCollectionOrdering(), CardsOrNotes.CARDS)
val cards: List<Card> = try {
searchForRows(apiContract.cardSuppliedData, SortOrder.UseCollectionOrdering(), CardsOrNotes.CARDS)
.map { withCol { getCard(it.cardOrNoteId) } }
} catch (exc: Exception) {
activity.webView!!.evaluateJavascript(
"console.log('${context.getString(R.string.search_card_js_api_no_results)}')",
Expand All @@ -398,13 +402,13 @@ open class AnkiDroidJsAPI(private val activity: AbstractFlashcardViewer) {
return@withContext convertToByteArray(apiContract, false)
}
val searchResult: MutableList<String> = ArrayList()
for (s in cards) {
for (card in cards) {
val jsonObject = JSONObject()
val fieldsData = s.card.note(getColUnsafe).fields
val fieldsName = s.card.noteType(getColUnsafe).fieldsNames
val fieldsData = card.note(getColUnsafe).fields
val fieldsName = card.noteType(getColUnsafe).fieldsNames

val noteId = s.card.nid
val cardId = s.card.id
val noteId = card.nid
val cardId = card.id
jsonObject.put("cardId", cardId)
jsonObject.put("noteId", noteId)

Expand Down
943 changes: 152 additions & 791 deletions AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt

Large diffs are not rendered by default.

6 changes: 5 additions & 1 deletion AnkiDroid/src/main/java/com/ichi2/anki/Flag.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import com.ichi2.libanki.Card
import com.ichi2.libanki.CardId
import com.ichi2.libanki.Collection
import org.json.JSONObject
import timber.log.Timber

enum class Flag(val code: Int, @DrawableRes val drawableRes: Int, @ColorRes val browserColorRes: Int?) {
NONE(0, R.drawable.ic_flag_transparent, null),
Expand Down Expand Up @@ -92,5 +93,8 @@ private value class FlagLabels(val value: JSONObject) {
}
}

fun Collection.setUserFlag(flag: Flag, cids: List<CardId>) = this.setUserFlag(flag.code, cids)
fun Collection.setUserFlag(flag: Flag, cids: List<CardId>) {
Timber.i("Flagging %d card(s) as %s", cids.size, flag)
this.setUserFlag(flag.code, cids)
}
fun Card.setUserFlag(flag: Flag) = this.setUserFlag(flag.code)
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright (c) 2024 David Allison <davidallisongithub@gmail.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/

package com.ichi2.anki.browser

import android.content.SharedPreferences
import androidx.annotation.CheckResult
import androidx.core.content.edit
import com.ichi2.anki.CardBrowser
import com.ichi2.anki.model.CardsOrNotes
import com.ichi2.anki.model.CardsOrNotes.CARDS
import com.ichi2.anki.model.CardsOrNotes.NOTES
import com.ichi2.libanki.BrowserConfig
import com.ichi2.libanki.BrowserConfig.ACTIVE_CARD_COLUMNS_KEY
import com.ichi2.libanki.BrowserConfig.ACTIVE_NOTE_COLUMNS_KEY
import com.ichi2.libanki.BrowserDefaults
import net.ankiweb.rsdroid.Backend
import timber.log.Timber

/**
* A collection of columns available in the [browser][CardBrowser]
*
* These are stored in [SharedPreferences] under either:
* * [ACTIVE_CARD_COLUMNS_KEY]
* * [ACTIVE_NOTE_COLUMNS_KEY]
*
* @see Backend.setActiveBrowserColumns
* @see BrowserConfig.activeColumnsKey
*/
class BrowserColumnCollection(val columns: List<CardBrowserColumn>) {
val backendKeys: Iterable<String> get() = columns.map { it.ankiColumnKey }
val count = columns.size
operator fun get(index: Int) = columns[index]

companion object {
private const val SEPARATOR_CHAR = '|'

@CheckResult
fun load(prefs: SharedPreferences, mode: CardsOrNotes): BrowserColumnCollection {
val key = mode.toPreferenceKey()
val columns = try {
val value = prefs.getString(key, mode.defaultColumns())!!
value.split(SEPARATOR_CHAR).map { CardBrowserColumn.fromColumnKey(it) }
} catch (e: Exception) {
Timber.w(e, "error loading columns, returning default")
val value = mode.defaultColumns()
value.split(SEPARATOR_CHAR).map { CardBrowserColumn.fromColumnKey(it) }
}
return BrowserColumnCollection(columns)
}

/**
* @param block Update the column list here. `null` meaning 'none'
*/
fun update(
prefs: SharedPreferences,
mode: CardsOrNotes,
block: (MutableList<CardBrowserColumn?>) -> Boolean
): BrowserColumnCollection? {
val valuesToUpdate: MutableList<CardBrowserColumn?> = load(prefs, mode).columns.toMutableList()
if (!block(valuesToUpdate)) {
Timber.d("no changes requested")
return null
}
// as in AnkiMobile, this converts: [QUESTION, NONE, TAGS] into [QUESTION, TAGS]
val updatedValues = valuesToUpdate.filterNotNull()
return BrowserColumnCollection(updatedValues).also {
save(prefs, mode, it)
}
}

fun save(prefs: SharedPreferences, mode: CardsOrNotes, value: BrowserColumnCollection) {
val key = mode.toPreferenceKey()
val preferenceValue = value.columns
.joinToString(separator = SEPARATOR_CHAR.toString()) { it.ankiColumnKey }
Timber.d("updating '%s' [%s] to '%s'", key, mode, preferenceValue)
prefs.edit { putString(key, preferenceValue) }
}

private fun CardsOrNotes.toPreferenceKey() =
BrowserConfig.activeColumnsKey(isNotesMode = this == NOTES)

private fun CardsOrNotes.defaultColumns() =
(if (this == CARDS) BrowserDefaults.CARD_COLUMNS else BrowserDefaults.NOTE_COLUMNS)
.joinToString(separator = SEPARATOR_CHAR.toString())
}
}
Loading
Loading