Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -2624,12 +2624,11 @@ abstract class AbstractFlashcardViewer :
get() = displayAnswer

internal fun showTagsDialog() {
val tags = ArrayList(getColUnsafe.tags.all())
val selTags = ArrayList(currentCard!!.note(getColUnsafe).tags)
val noteId = currentCard!!.note(getColUnsafe).id
val dialog =
tagsDialogFactory!!
.newTagsDialog()
.withArguments(this, TagsDialog.DialogType.EDIT_TAGS, selTags, tags)
.withArguments(this, TagsDialog.DialogType.EDIT_TAGS, noteIds = listOf(noteId))
showDialogFragment(dialog)
}

Expand Down
106 changes: 17 additions & 89 deletions AnkiDroid/src/main/java/com/ichi2/anki/CardBrowser.kt
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ import com.ichi2.anki.browser.CardBrowserViewModel.SearchState.Searching
import com.ichi2.anki.browser.CardOrNoteId
import com.ichi2.anki.browser.ColumnHeading
import com.ichi2.anki.browser.FindAndReplaceDialogFragment
import com.ichi2.anki.browser.PreviewerIdsFile
import com.ichi2.anki.browser.IdsFile
import com.ichi2.anki.browser.RepositionCardFragment
import com.ichi2.anki.browser.RepositionCardFragment.Companion.REQUEST_REPOSITION_NEW_CARDS
import com.ichi2.anki.browser.RepositionCardsRequest.ContainsNonNewCardsError
Expand Down Expand Up @@ -120,7 +120,6 @@ import com.ichi2.libanki.ChangeManager
import com.ichi2.libanki.Collection
import com.ichi2.libanki.DeckId
import com.ichi2.libanki.DeckNameId
import com.ichi2.libanki.NoteId
import com.ichi2.libanki.SortOrder
import com.ichi2.libanki.undoableOp
import com.ichi2.ui.CardBrowserSearchView
Expand All @@ -130,10 +129,8 @@ import com.ichi2.utils.dp
import com.ichi2.utils.increaseHorizontalPaddingOfOverflowMenuIcons
import com.ichi2.utils.updatePaddingRelative
import com.ichi2.widget.WidgetStatus.updateInBackground
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.ankiweb.rsdroid.RustCleanup
import net.ankiweb.rsdroid.Translations
import timber.log.Timber
Expand Down Expand Up @@ -1437,14 +1434,14 @@ open class CardBrowser :
private fun onPreview() {
launchCatchingTask {
val intentData = viewModel.queryPreviewIntentData()
onPreviewCardsActivityResult.launch(getPreviewIntent(intentData.currentIndex, intentData.previewerIdsFile))
onPreviewCardsActivityResult.launch(getPreviewIntent(intentData.currentIndex, intentData.idsFile))
}
}

private fun getPreviewIntent(
index: Int,
previewerIdsFile: PreviewerIdsFile,
): Intent = PreviewerDestination(index, previewerIdsFile).toIntent(this)
idsFile: IdsFile,
): Intent = PreviewerDestination(index, idsFile).toIntent(this)

private fun rescheduleSelectedCards() {
if (!viewModel.hasSelectedAnyRows()) {
Expand Down Expand Up @@ -1503,84 +1500,16 @@ open class CardBrowser :
if (!viewModel.hasSelectedAnyRows()) {
Timber.d("showEditTagsDialog: called with empty selection")
}

var progressMax: Int? = null // this can be made null to blank the dialog
var progress = 0

fun onProgress(progressContext: ProgressContext) {
val max = progressMax
if (max == null) {
progressContext.amount = null
progressContext.text = getString(R.string.dialog_processing)
} else {
progressContext.amount = Pair(progress, max)
}
}
launchCatchingTask {
withProgress(extractProgress = ::onProgress) {
val allTags = withCol { tags.all() }
val selectedNoteIds = viewModel.queryAllSelectedNoteIds()

progressMax = selectedNoteIds.size * 2
// TODO!! This is terribly slow on AnKing
val checkedTags =
withCol {
selectedNoteIds
.asSequence() // reduce memory pressure
.flatMap { nid ->
progress++
getNote(nid).tags // requires withCol
}.distinct()
.toList()
}

if (selectedNoteIds.size == 1) {
Timber.d("showEditTagsDialog: edit tags for one note")
tagsDialogListenerAction = TagsDialogListenerAction.EDIT_TAGS
val dialog =
tagsDialogFactory.newTagsDialog().withArguments(
this@CardBrowser,
type = TagsDialog.DialogType.EDIT_TAGS,
checkedTags = checkedTags,
allTags = allTags,
)
showDialogFragment(dialog)
return@withProgress
}
// TODO!! This is terribly slow on AnKing
// PERF: This MUST be combined with the above sequence - this becomes O(2n) on a
// database operation performed over 30k times
val uncheckedTags =
withCol {
selectedNoteIds
.asSequence() // reduce memory pressure
.flatMap { nid: NoteId ->
progress++
val note = getNote(nid) // requires withCol
val noteTags = note.tags.toSet()
allTags.filter { t: String? -> !noteTags.contains(t) }
}.distinct()
.toList()
}

progressMax = null

Timber.d("showEditTagsDialog: edit tags for multiple note")
tagsDialogListenerAction = TagsDialogListenerAction.EDIT_TAGS

// withArguments performs IO, can be 18 seconds
val dialog =
withContext(Dispatchers.IO) {
tagsDialogFactory.newTagsDialog().withArguments(
context = this@CardBrowser,
type = TagsDialog.DialogType.EDIT_TAGS,
checkedTags = checkedTags,
uncheckedTags = uncheckedTags,
allTags = allTags,
)
}
showDialogFragment(dialog)
}
tagsDialogListenerAction = TagsDialogListenerAction.EDIT_TAGS
lifecycleScope.launch {
val noteIds = viewModel.queryAllSelectedNoteIds()
val dialog =
tagsDialogFactory.newTagsDialog().withArguments(
this@CardBrowser,
type = TagsDialog.DialogType.EDIT_TAGS,
noteIds = noteIds,
)
showDialogFragment(dialog)
}
}

Expand All @@ -1591,8 +1520,7 @@ open class CardBrowser :
tagsDialogFactory.newTagsDialog().withArguments(
context = this@CardBrowser,
type = TagsDialog.DialogType.FILTER_BY_TAG,
checkedTags = ArrayList(0),
allTags = withCol { tags.all() },
noteIds = emptyList(),
)
showDialogFragment(dialog)
}
Expand Down Expand Up @@ -1950,8 +1878,8 @@ suspend fun searchForRows(

class PreviewerDestination(
val currentIndex: Int,
val previewerIdsFile: PreviewerIdsFile,
val idsFile: IdsFile,
)

@CheckResult
fun PreviewerDestination.toIntent(context: Context) = PreviewerFragment.getIntent(context, previewerIdsFile, currentIndex)
fun PreviewerDestination.toIntent(context: Context) = PreviewerFragment.getIntent(context, idsFile, currentIndex)
7 changes: 1 addition & 6 deletions AnkiDroid/src/main/java/com/ichi2/anki/NoteEditor.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1605,18 +1605,13 @@ class NoteEditor :
}

private fun showTagsDialog() {
if (selectedTags == null) {
selectedTags = ArrayList(0)
}
val tags = ArrayList(getColUnsafe.tags.all())
val selTags = ArrayList(selectedTags!!)
val selTags = selectedTags?.let { ArrayList(it) } ?: arrayListOf()
val dialog =
with(requireContext()) {
tagsDialogFactory!!.newTagsDialog().withArguments(
context = this,
type = TagsDialog.DialogType.EDIT_TAGS,
checkedTags = selTags,
allTags = tags,
)
}
showDialogFragment(dialog)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -881,11 +881,11 @@ class CardBrowserViewModel(
suspend fun queryPreviewIntentData(): PreviewerDestination {
// If in NOTES mode, we show one Card per Note, as this matches Anki Desktop
return if (selectedRowCount() > 1) {
PreviewerDestination(currentIndex = 0, PreviewerIdsFile(cacheDir, queryAllSelectedCardIds()))
PreviewerDestination(currentIndex = 0, IdsFile(cacheDir, queryAllSelectedCardIds()))
} else {
// Preview all cards, starting from the one that is currently selected
val startIndex = indexOfFirstCheckedCard() ?: 0
PreviewerDestination(startIndex, PreviewerIdsFile(cacheDir, queryOneCardIdPerNote()))
PreviewerDestination(startIndex, IdsFile(cacheDir, queryOneCardIdPerNote()))
}
}

Expand Down Expand Up @@ -1135,26 +1135,28 @@ enum class SaveSearchResult {
}

/**
* Temporary file containing the IDs of the cards to be displayed at the previewer
* Temporary file containing cards or note IDs to be passed in a Bundle.
*
* It avoids [android.os.TransactionTooLargeException] when passing a big amount of data.
*/
class PreviewerIdsFile(
class IdsFile(
path: String,
) : File(path),
Parcelable {
/**
* @param directory parent directory of the file. Generally it should be the cache directory
* @param cardIds ids of the cards to be displayed
* @param ids ids to store
*/
constructor(directory: File, cardIds: List<CardId>) : this(createTempFile("previewerIds", ".tmp", directory).path) {
constructor(directory: File, ids: List<Long>) : this(createTempFile("ids", ".tmp", directory).path) {
DataOutputStream(FileOutputStream(this)).use { outputStream ->
outputStream.writeInt(cardIds.size)
for (id in cardIds) {
outputStream.writeInt(ids.size)
for (id in ids) {
outputStream.writeLong(id)
}
}
}

fun getCardIds(): List<Long> =
fun getIds(): List<Long> =
DataInputStream(FileInputStream(this)).use { inputStream ->
val size = inputStream.readInt()
List(size) { inputStream.readLong() }
Expand All @@ -1173,10 +1175,10 @@ class PreviewerIdsFile(
@JvmField
@Suppress("unused")
val CREATOR =
object : Parcelable.Creator<PreviewerIdsFile> {
override fun createFromParcel(source: Parcel?): PreviewerIdsFile = PreviewerIdsFile(source!!.readString()!!)
object : Parcelable.Creator<IdsFile> {
override fun createFromParcel(source: Parcel?): IdsFile = IdsFile(source!!.readString()!!)

override fun newArray(size: Int): Array<PreviewerIdsFile> = arrayOf()
override fun newArray(size: Int): Array<IdsFile> = arrayOf()
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package com.ichi2.anki.dialogs.tags

import android.content.res.Resources
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
Expand Down Expand Up @@ -45,7 +44,6 @@ import java.util.TreeSet
*/
class TagsArrayAdapter(
private val tags: TagsList,
private val resources: Resources,
) : RecyclerView.Adapter<TagsArrayAdapter.ViewHolder>(),
Filterable {
class ViewHolder(
Expand Down Expand Up @@ -302,7 +300,7 @@ class TagsArrayAdapter(
// do not add padding if there is no visible nested tag
holder.expandButton.visibility = View.GONE
}
holder.expandButton.contentDescription = resources.getString(R.string.expand_tag, holder.node.tag.replace("::", " "))
holder.expandButton.contentDescription = holder.itemView.context.getString(R.string.expand_tag, holder.node.tag.replace("::", " "))

holder.textView.text = TagsUtil.getTagParts(holder.node.tag).last()

Expand Down
Loading