mirror of
https://github.com/ankidroid/Anki-Android.git
synced 2024-09-19 19:42:17 +02:00
remove Kotlin's "Context Receiver" feature
Context Receivers are an experimental Kotlin feature . They will be removed and replaced with Context Parameters. It is unlikely that there will be any overlap period, so we need to remove our Context Receiver code, with the intention of replacing it at a later date > * **2.0.20**: A warning is introduced for declarations that use > context receivers. > The warning should mention that the design of context receivers > is about to change, according to the new design (add a link to the KEEP/YT). > The safest workaround is to migrate your code to extensions. > * **2.1.20**: The implementation of context receivers is removed from the compiler. > Effectively, this warning is turned into an error. > Note that this part is most likely inevitable as the implementation > of context parameters requires quite extensive refactoring in the compiler, > and there are significant chances that we won't have either > context receivers or context parameters at that time. > [There is a slight chance that we'll have an early prototype of context parameters, > but we are not counting on that.] > * **2.2.0**: Context parameters are introduced under a new compiler flag. https://youtrack.jetbrains.com/issue/KT-67119/Migration-warning-from-context-receivers-to-context-parameters > **Phased removal of context receivers feature** > In Kotlin 1.6.20, we introduced context receivers as an > Experimental feature. After listening to community > feedback, we've decided not to continue with it. Instead, > we plan to introduce a replacement in future Kotlin > releases: context parameters. You can find the proposal > for context parameters in the KEEP. https://kotlinlang.org/docs/whatsnew-eap.html#phased-removal-of-context-receivers-feature Deprecated: https://redirect.github.com/Kotlin/KEEP/blob/context-parameters/proposals/context-receivers.md * https://redirect.github.com/Kotlin/KEEP/issues/259 To be replaced by: https://redirect.github.com/Kotlin/KEEP/blob/context-parameters/proposals/context-parameters.md * https://redirect.github.com/Kotlin/KEEP/issues/367 Fixes 16863 Reverts:672b075a54
Reverts:1c01e4cb6f
This commit is contained in:
parent
35cfd4de33
commit
06e8ac2ef3
@ -1330,7 +1330,7 @@ class ContentProviderTest : InstrumentedTest() {
|
||||
fields: Array<String>,
|
||||
tag: String
|
||||
): Uri {
|
||||
val newNote = col.run { Note.fromNotetypeId(mid) }
|
||||
val newNote = Note.fromNotetypeId(col, mid)
|
||||
for (idx in fields.indices) {
|
||||
newNote.setField(idx, fields[idx])
|
||||
}
|
||||
|
@ -145,9 +145,6 @@ import com.ichi2.libanki.Sound.getAvTag
|
||||
import com.ichi2.libanki.SoundOrVideoTag
|
||||
import com.ichi2.libanki.TTSTag
|
||||
import com.ichi2.libanki.Utils
|
||||
import com.ichi2.libanki.note
|
||||
import com.ichi2.libanki.renderOutput
|
||||
import com.ichi2.libanki.setTagsFromStr
|
||||
import com.ichi2.libanki.undoableOp
|
||||
import com.ichi2.themes.Themes
|
||||
import com.ichi2.themes.Themes.getResFromAttr
|
||||
@ -519,7 +516,7 @@ abstract class AbstractFlashcardViewer :
|
||||
// despite that making no sense outside of Reviewer.kt
|
||||
currentCard = withCol {
|
||||
sched.card?.apply {
|
||||
renderOutput()
|
||||
renderOutput(this@withCol, reload = false, browser = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2612,7 +2609,7 @@ abstract class AbstractFlashcardViewer :
|
||||
val tags = ArrayList(getColUnsafe.tags.all())
|
||||
val selTags = ArrayList(currentCard!!.note(getColUnsafe).tags)
|
||||
val dialog = tagsDialogFactory!!.newTagsDialog()
|
||||
.withArguments(TagsDialog.DialogType.EDIT_TAGS, selTags, tags)
|
||||
.withArguments(this, TagsDialog.DialogType.EDIT_TAGS, selTags, tags)
|
||||
showDialogFragment(dialog)
|
||||
}
|
||||
|
||||
@ -2622,10 +2619,10 @@ abstract class AbstractFlashcardViewer :
|
||||
stateFilter: CardStateFilter
|
||||
) {
|
||||
launchCatchingTask {
|
||||
val note = withCol { currentCard!!.note() }
|
||||
val note = withCol { currentCard!!.note(this@withCol) }
|
||||
if (note.tags == selectedTags) return@launchCatchingTask
|
||||
|
||||
withCol { note.setTagsFromStr(selectedTags.joinToString(" ")) }
|
||||
withCol { note.setTagsFromStr(this@withCol, selectedTags.joinToString(" ")) }
|
||||
undoableOp { updateNote(note) }
|
||||
// Reload current card to reflect tag changes
|
||||
reloadWebViewContent()
|
||||
|
@ -267,13 +267,12 @@ open class BackupManager {
|
||||
*
|
||||
* @return whether the repair was successful
|
||||
*/
|
||||
context (Collection)
|
||||
fun repairCollection(): Boolean {
|
||||
val colPath = this@Collection.path
|
||||
fun repairCollection(col: Collection): Boolean {
|
||||
val colPath = col.path
|
||||
val colFile = File(colPath)
|
||||
val time = TimeManager.time
|
||||
Timber.i("BackupManager - RepairCollection - Closing Collection")
|
||||
this@Collection.close()
|
||||
col.close()
|
||||
|
||||
// repair file
|
||||
val execString = "sqlite3 $colPath .dump | sqlite3 $colPath.tmp"
|
||||
@ -460,10 +459,9 @@ open class BackupManager {
|
||||
*
|
||||
* @return Whether all specified backups were successfully deleted.
|
||||
*/
|
||||
context (Collection)
|
||||
@Throws(IllegalArgumentException::class)
|
||||
fun deleteBackups(backupsToDelete: List<File>): Boolean {
|
||||
val allBackups = getBackups(File(this@Collection.path))
|
||||
fun deleteBackups(collection: Collection, backupsToDelete: List<File>): Boolean {
|
||||
val allBackups = getBackups(File(collection.path))
|
||||
val invalidBackupsToDelete = backupsToDelete.toSet() - allBackups.toSet()
|
||||
|
||||
if (invalidBackupsToDelete.isNotEmpty()) {
|
||||
|
@ -128,7 +128,6 @@ import com.ichi2.libanki.SortOrder
|
||||
import com.ichi2.libanki.Sound
|
||||
import com.ichi2.libanki.TemplateManager
|
||||
import com.ichi2.libanki.Utils
|
||||
import com.ichi2.libanki.setTagsFromStr
|
||||
import com.ichi2.libanki.stripAvRefs
|
||||
import com.ichi2.libanki.undoableOp
|
||||
import com.ichi2.libanki.utils.TimeManager
|
||||
@ -886,7 +885,7 @@ open class CardBrowser :
|
||||
}
|
||||
|
||||
actionBarMenu?.findItem(R.id.action_reschedule_cards)?.title =
|
||||
TR.actionsSetDueDate().toSentenceCase(R.string.sentence_set_due_date)
|
||||
TR.actionsSetDueDate().toSentenceCase(this, R.string.sentence_set_due_date)
|
||||
|
||||
previewItem = menu.findItem(R.id.action_preview)
|
||||
onSelectionChanged()
|
||||
@ -936,12 +935,12 @@ open class CardBrowser :
|
||||
}
|
||||
if (viewModel.hasSelectedAnyRows()) {
|
||||
actionBarMenu.findItem(R.id.action_suspend_card).apply {
|
||||
title = TR.browsingToggleSuspend().toSentenceCase(R.string.sentence_toggle_suspend)
|
||||
title = TR.browsingToggleSuspend().toSentenceCase(this@CardBrowser, R.string.sentence_toggle_suspend)
|
||||
// TODO: I don't think this icon is necessary
|
||||
setIcon(R.drawable.ic_suspend)
|
||||
}
|
||||
actionBarMenu.findItem(R.id.action_toggle_bury).apply {
|
||||
title = TR.browsingToggleBury().toSentenceCase(R.string.sentence_toggle_bury)
|
||||
title = TR.browsingToggleBury().toSentenceCase(this@CardBrowser, R.string.sentence_toggle_bury)
|
||||
}
|
||||
actionBarMenu.findItem(R.id.action_mark_card).apply {
|
||||
title = TR.browsingToggleMark()
|
||||
@ -1347,6 +1346,7 @@ open class CardBrowser :
|
||||
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
|
||||
@ -1378,6 +1378,7 @@ open class CardBrowser :
|
||||
// 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,
|
||||
@ -1392,9 +1393,10 @@ open class CardBrowser :
|
||||
private fun showFilterByTagsDialog() {
|
||||
tagsDialogListenerAction = TagsDialogListenerAction.FILTER
|
||||
val dialog = tagsDialogFactory.newTagsDialog().withArguments(
|
||||
TagsDialog.DialogType.FILTER_BY_TAG,
|
||||
ArrayList(0),
|
||||
getColUnsafe.tags.all()
|
||||
context = this@CardBrowser,
|
||||
type = TagsDialog.DialogType.FILTER_BY_TAG,
|
||||
checkedTags = ArrayList(0),
|
||||
allTags = getColUnsafe.tags.all()
|
||||
)
|
||||
showDialogFragment(dialog)
|
||||
}
|
||||
@ -1552,7 +1554,7 @@ open class CardBrowser :
|
||||
.onEach { note ->
|
||||
val previousTags: List<String> = note.tags
|
||||
val updatedTags = getUpdatedTags(previousTags, selectedTags, indeterminateTags)
|
||||
note.setTagsFromStr(tags.join(updatedTags))
|
||||
note.setTagsFromStr(this@undoableOp, tags.join(updatedTags))
|
||||
}
|
||||
updateNotes(selectedNotes)
|
||||
}
|
||||
@ -2339,14 +2341,13 @@ suspend fun searchForCards(
|
||||
): MutableList<CardBrowser.CardCache> {
|
||||
return withCol {
|
||||
(if (cardsOrNotes == CARDS) findCards(query, order) else findOneCardByNote(query, order)).asSequence()
|
||||
.toCardCache(cardsOrNotes)
|
||||
.toCardCache(this@withCol, cardsOrNotes)
|
||||
.toMutableList()
|
||||
}
|
||||
}
|
||||
|
||||
context (Collection)
|
||||
private fun Sequence<CardId>.toCardCache(isInCardMode: CardsOrNotes): Sequence<CardBrowser.CardCache> {
|
||||
return this.mapIndexed { idx, cid -> CardBrowser.CardCache(cid, this@Collection, idx, isInCardMode) }
|
||||
private fun Sequence<CardId>.toCardCache(col: Collection, isInCardMode: CardsOrNotes): Sequence<CardBrowser.CardCache> {
|
||||
return this.mapIndexed { idx, cid -> CardBrowser.CardCache(cid, col, idx, isInCardMode) }
|
||||
}
|
||||
|
||||
class PreviewerDestination(val currentIndex: Int, val previewerIdsFile: PreviewerIdsFile)
|
||||
|
@ -191,7 +191,7 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
|
||||
val notetype = tempModel!!.notetype
|
||||
val notetypeFile = NotetypeFile(this@CardTemplateEditor, notetype)
|
||||
val ord = viewPager.currentItem
|
||||
val note = withCol { currentFragment?.getNote(this) ?: Note.fromNotetypeId(notetype.id) }
|
||||
val note = withCol { currentFragment?.getNote(this) ?: Note.fromNotetypeId(this@withCol, notetype.id) }
|
||||
val args = TemplatePreviewerArguments(
|
||||
notetypeFile = notetypeFile,
|
||||
id = note.id,
|
||||
@ -736,7 +736,7 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
|
||||
}
|
||||
launchCatchingTask(resources.getString(R.string.card_template_editor_save_error)) {
|
||||
requireActivity().withProgress(resources.getString(R.string.saving_model)) {
|
||||
withCol { templateEditor.tempModel!!.saveToDatabase() }
|
||||
withCol { templateEditor.tempModel!!.saveToDatabase(this@withCol) }
|
||||
}
|
||||
onModelSaved()
|
||||
}
|
||||
@ -889,7 +889,7 @@ open class CardTemplateEditor : AnkiActivity(), DeckSelectionListener {
|
||||
val notetype = templateEditor.tempModel!!.notetype
|
||||
val notetypeFile = NotetypeFile(requireContext(), notetype)
|
||||
val ord = templateEditor.viewPager.currentItem
|
||||
val note = withCol { getNote(this) ?: Note.fromNotetypeId(notetype.id) }
|
||||
val note = withCol { getNote(this) ?: Note.fromNotetypeId(this@withCol, notetype.id) }
|
||||
val args = TemplatePreviewerArguments(
|
||||
notetypeFile = notetypeFile,
|
||||
id = note.id,
|
||||
|
@ -91,12 +91,11 @@ class CardTemplateNotetype(val notetype: NotetypeJson) {
|
||||
addTemplateChange(ChangeType.DELETE, ord)
|
||||
}
|
||||
|
||||
context(Collection)
|
||||
fun saveToDatabase() {
|
||||
fun saveToDatabase(col: Collection) {
|
||||
Timber.d("saveToDatabase() called")
|
||||
dumpChanges()
|
||||
clearTempModelFiles()
|
||||
return saveModel(notetype, adjustedTemplateChanges)
|
||||
return saveModel(col, notetype, adjustedTemplateChanges)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1904,7 +1904,7 @@ open class DeckPicker :
|
||||
withCol {
|
||||
Timber.i("RepairCollection: Closing collection")
|
||||
close()
|
||||
BackupManager.repairCollection()
|
||||
BackupManager.repairCollection(this@withCol)
|
||||
}
|
||||
}
|
||||
if (!result) {
|
||||
@ -1963,7 +1963,7 @@ open class DeckPicker :
|
||||
launchCatchingTask {
|
||||
// Number of deleted files
|
||||
val noOfDeletedFiles = withProgress(resources.getString(R.string.delete_media_message)) {
|
||||
withCol { deleteMedia(unused) }
|
||||
withCol { deleteMedia(this@withCol, unused) }
|
||||
}
|
||||
showSimpleMessageDialog(
|
||||
title = resources.getString(R.string.delete_media_result_title),
|
||||
|
@ -108,6 +108,7 @@ import com.ichi2.anki.noteeditor.CustomToolbarButton
|
||||
import com.ichi2.anki.noteeditor.FieldState
|
||||
import com.ichi2.anki.noteeditor.FieldState.FieldChangeType
|
||||
import com.ichi2.anki.noteeditor.NoteEditorLauncher
|
||||
import com.ichi2.anki.noteeditor.Toolbar
|
||||
import com.ichi2.anki.noteeditor.Toolbar.TextFormatListener
|
||||
import com.ichi2.anki.noteeditor.Toolbar.TextWrapper
|
||||
import com.ichi2.anki.pages.ImageOcclusion
|
||||
@ -139,8 +140,6 @@ import com.ichi2.libanki.NotetypeJson
|
||||
import com.ichi2.libanki.Notetypes
|
||||
import com.ichi2.libanki.Notetypes.Companion.NOT_FOUND_NOTE_TYPE
|
||||
import com.ichi2.libanki.Utils
|
||||
import com.ichi2.libanki.load
|
||||
import com.ichi2.libanki.note
|
||||
import com.ichi2.libanki.undoableOp
|
||||
import com.ichi2.utils.ClipboardUtil
|
||||
import com.ichi2.utils.HashUtil
|
||||
@ -168,12 +167,10 @@ import java.io.File
|
||||
import java.util.LinkedList
|
||||
import java.util.Locale
|
||||
import java.util.function.Consumer
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
import androidx.appcompat.widget.Toolbar as MainToolbar
|
||||
import com.ichi2.anki.noteeditor.Toolbar as Toolbar
|
||||
|
||||
/**
|
||||
* Allows the user to edit a note, for instance if there is a typo. A card is a presentation of a note, and has two
|
||||
@ -1175,7 +1172,7 @@ class NoteEditor : AnkiFragment(R.layout.note_editor), DeckSelectionListener, Su
|
||||
if (caller == CALLER_PREVIEWER_EDIT || caller == CALLER_EDIT) {
|
||||
withProgress {
|
||||
undoableOp {
|
||||
updateNote(currentEditedCard!!.note())
|
||||
updateNote(currentEditedCard!!.note(this@undoableOp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1196,7 +1193,7 @@ class NoteEditor : AnkiFragment(R.layout.note_editor), DeckSelectionListener, Su
|
||||
notetypes.change(oldNotetype, noteId, newNotetype, modelChangeFieldMap!!, modelChangeCardMap!!)
|
||||
}
|
||||
// refresh the note object to reflect the database changes
|
||||
withCol { editorNote!!.load() }
|
||||
withCol { editorNote!!.load(this@withCol) }
|
||||
// close note editor
|
||||
closeNoteEditor()
|
||||
}
|
||||
@ -1377,7 +1374,7 @@ class NoteEditor : AnkiFragment(R.layout.note_editor), DeckSelectionListener, Su
|
||||
val tags = selectedTags ?: mutableListOf()
|
||||
|
||||
val ord = if (editorNote!!.notetype.isCloze) {
|
||||
val tempNote = withCol { Note.fromNotetypeId(editorNote!!.notetype.id) }
|
||||
val tempNote = withCol { Note.fromNotetypeId(this@withCol, editorNote!!.notetype.id) }
|
||||
tempNote.fields = fields // makes possible to get the cloze numbers from the fields
|
||||
val clozeNumbers = withCol { clozeNumbersInNote(tempNote) }
|
||||
if (clozeNumbers.isNotEmpty()) {
|
||||
@ -1485,7 +1482,12 @@ class NoteEditor : AnkiFragment(R.layout.note_editor), DeckSelectionListener, Su
|
||||
val tags = ArrayList(getColUnsafe.tags.all())
|
||||
val selTags = ArrayList(selectedTags!!)
|
||||
val dialog = with(requireContext()) {
|
||||
tagsDialogFactory!!.newTagsDialog().withArguments(TagsDialog.DialogType.EDIT_TAGS, selTags, tags)
|
||||
tagsDialogFactory!!.newTagsDialog().withArguments(
|
||||
context = this,
|
||||
type = TagsDialog.DialogType.EDIT_TAGS,
|
||||
checkedTags = selTags,
|
||||
allTags = tags
|
||||
)
|
||||
}
|
||||
showDialogFragment(dialog)
|
||||
}
|
||||
@ -1544,7 +1546,7 @@ class NoteEditor : AnkiFragment(R.layout.note_editor), DeckSelectionListener, Su
|
||||
private suspend fun getCurrentMultimediaEditableNote(): MultimediaEditableNote? {
|
||||
val note = NoteService.createEmptyNote(editorNote!!.notetype)
|
||||
val fields = currentFieldStrings.requireNoNulls()
|
||||
withCol { NoteService.updateMultimediaNoteFromFields(fields, editorNote!!.mid, note!!) }
|
||||
withCol { NoteService.updateMultimediaNoteFromFields(this@withCol, fields, editorNote!!.mid, note!!) }
|
||||
|
||||
return note
|
||||
}
|
||||
@ -2091,7 +2093,7 @@ class NoteEditor : AnkiFragment(R.layout.note_editor), DeckSelectionListener, Su
|
||||
editorNote = if (note == null || addNote) {
|
||||
getColUnsafe.run {
|
||||
val notetype = notetypes.current()
|
||||
Note.fromNotetypeId(notetype.id)
|
||||
Note.fromNotetypeId(this@run, notetype.id)
|
||||
}
|
||||
} else {
|
||||
note
|
||||
|
@ -98,7 +98,6 @@ import com.ichi2.libanki.Card
|
||||
import com.ichi2.libanki.CardId
|
||||
import com.ichi2.libanki.Collection
|
||||
import com.ichi2.libanki.Consts
|
||||
import com.ichi2.libanki.renderOutput
|
||||
import com.ichi2.libanki.sched.Counts
|
||||
import com.ichi2.libanki.sched.CurrentQueueState
|
||||
import com.ichi2.libanki.undoableOp
|
||||
@ -741,7 +740,7 @@ open class Reviewer :
|
||||
|
||||
// Anki Desktop Translations
|
||||
menu.findItem(R.id.action_reschedule_card).title =
|
||||
CollectionManager.TR.actionsSetDueDate().toSentenceCase(R.string.sentence_set_due_date)
|
||||
CollectionManager.TR.actionsSetDueDate().toSentenceCase(this, R.string.sentence_set_due_date)
|
||||
|
||||
// Undo button
|
||||
@DrawableRes val undoIconId: Int
|
||||
@ -1061,7 +1060,7 @@ open class Reviewer :
|
||||
override suspend fun updateCurrentCard() {
|
||||
val state = withCol {
|
||||
sched.currentQueueState()?.apply {
|
||||
topCard.renderOutput(true)
|
||||
topCard.renderOutput(this@withCol, reload = true)
|
||||
}
|
||||
}
|
||||
state?.timeboxReached?.let { dealWithTimeBox(it) }
|
||||
|
@ -296,7 +296,7 @@ class StudyOptionsFragment : Fragment(), ChangeManager.Subscriber, Toolbar.OnMen
|
||||
withCol {
|
||||
Timber.d("doInBackground - RebuildCram")
|
||||
sched.rebuildDyn(decks.selected())
|
||||
updateValuesFromDeck()
|
||||
updateValuesFromDeck(this@withCol)
|
||||
}
|
||||
}
|
||||
rebuildUi(result, true)
|
||||
@ -308,7 +308,7 @@ class StudyOptionsFragment : Fragment(), ChangeManager.Subscriber, Toolbar.OnMen
|
||||
withCol {
|
||||
Timber.d("doInBackgroundEmptyCram")
|
||||
sched.emptyDyn(decks.selected())
|
||||
updateValuesFromDeck()
|
||||
updateValuesFromDeck(this@withCol)
|
||||
}
|
||||
}
|
||||
rebuildUi(result, true)
|
||||
@ -486,7 +486,7 @@ class StudyOptionsFragment : Fragment(), ChangeManager.Subscriber, Toolbar.OnMen
|
||||
// Load the deck counts for the deck from Collection asynchronously
|
||||
updateValuesFromDeckJob = launchCatchingTask {
|
||||
if (CollectionManager.isOpenUnsafe()) {
|
||||
val result = withCol { updateValuesFromDeck() }
|
||||
val result = withCol { updateValuesFromDeck(this@withCol) }
|
||||
rebuildUi(result, resetDecklist)
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,10 @@
|
||||
|
||||
package com.ichi2.anki
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.util.DisplayMetrics
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.ichi2.libanki.utils.Time
|
||||
import java.util.Calendar
|
||||
|
||||
@ -23,16 +21,6 @@ fun showThemedToast(context: Context, @StringRes textResource: Int, shortLength:
|
||||
Toast.makeText(context, textResource, if (shortLength) Toast.LENGTH_SHORT else Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
context (Fragment)
|
||||
fun showThemedToast(@StringRes textResource: Int, shortLength: Boolean) {
|
||||
Toast.makeText(requireContext(), textResource, if (shortLength) Toast.LENGTH_SHORT else Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
context (Activity)
|
||||
fun showThemedToast(@StringRes textResource: Int, shortLength: Boolean) {
|
||||
Toast.makeText(this@Activity, textResource, if (shortLength) Toast.LENGTH_SHORT else Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
fun getDensityAdjustedValue(context: Context, value: Float): Float {
|
||||
return context.resources.displayMetrics.density * value
|
||||
}
|
||||
|
@ -52,7 +52,6 @@ import com.ichi2.libanki.Consts.QUEUE_TYPE_MANUALLY_BURIED
|
||||
import com.ichi2.libanki.Consts.QUEUE_TYPE_SIBLING_BURIED
|
||||
import com.ichi2.libanki.DeckId
|
||||
import com.ichi2.libanki.NoteId
|
||||
import com.ichi2.libanki.hasTag
|
||||
import com.ichi2.libanki.undoableOp
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.Job
|
||||
@ -311,7 +310,7 @@ class CardBrowserViewModel(
|
||||
val initialDeckId = if (selectAllDecks) ALL_DECKS_ID else getInitialDeck()
|
||||
// PERF: slightly inefficient if the source was lastDeckId
|
||||
setDeckId(initialDeckId)
|
||||
val cardsOrNotes = withCol { CardsOrNotes.fromCollection() }
|
||||
val cardsOrNotes = withCol { CardsOrNotes.fromCollection(this@withCol) }
|
||||
flowOfCardsOrNotes.update { cardsOrNotes }
|
||||
|
||||
val allColumns = withCol { allBrowserColumns() }.associateBy { it.key }
|
||||
@ -368,7 +367,7 @@ class CardBrowserViewModel(
|
||||
// if all notes are marked, remove the mark
|
||||
// if no notes are marked, add the mark
|
||||
// if there is a mix, enable the mark on all
|
||||
val wantMark = !noteIds.all { getNote(it).hasTag("marked") }
|
||||
val wantMark = !noteIds.all { getNote(it).hasTag(this@undoableOp, "marked") }
|
||||
Timber.i("setting mark = %b for %d notes", wantMark, noteIds.size)
|
||||
if (wantMark) {
|
||||
tags.bulkAdd(noteIds, "marked")
|
||||
@ -399,7 +398,7 @@ class CardBrowserViewModel(
|
||||
Timber.i("setting mode to %s", newValue)
|
||||
withCol {
|
||||
// Change this to only change the preference on a state change
|
||||
newValue.saveToCollection()
|
||||
newValue.saveToCollection(this@withCol)
|
||||
}
|
||||
flowOfCardsOrNotes.update { newValue }
|
||||
setupColumns(newValue)
|
||||
|
@ -144,7 +144,7 @@ class CardMediaPlayer : Closeable {
|
||||
this.answers = renderOutput.answerAvTags
|
||||
|
||||
if (!this::config.isInitialized || !config.appliesTo(card)) {
|
||||
config = withCol { CardSoundConfig.create(card) }
|
||||
config = withCol { CardSoundConfig.create(this@withCol, card) }
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,7 +162,7 @@ class CardMediaPlayer : Closeable {
|
||||
this.answers = renderOutput.answerAvTags
|
||||
|
||||
if (!this::config.isInitialized || !config.appliesTo(card)) {
|
||||
config = withCol { CardSoundConfig.create(card) }
|
||||
config = withCol { CardSoundConfig.create(this@withCol, card) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,14 +37,13 @@ class CardSoundConfig(val replayQuestion: Boolean, val autoplay: Boolean, val de
|
||||
fun appliesTo(card: Card): Boolean = CardUtils.getDeckIdForCard(card) == deckId
|
||||
|
||||
companion object {
|
||||
context(Collection)
|
||||
@CheckResult
|
||||
fun create(card: Card): CardSoundConfig {
|
||||
fun create(col: Collection, card: Card): CardSoundConfig {
|
||||
Timber.v("start loading SoundConfig")
|
||||
|
||||
val autoPlay = card.autoplay(this@Collection)
|
||||
val autoPlay = card.autoplay(col)
|
||||
|
||||
val replayQuestion: Boolean = card.replayQuestionAudioOnAnswerSide(this@Collection)
|
||||
val replayQuestion: Boolean = card.replayQuestionAudioOnAnswerSide(col)
|
||||
|
||||
return CardSoundConfig(replayQuestion, autoPlay, card.did).apply {
|
||||
Timber.d("loaded SoundConfig: %s", this)
|
||||
|
@ -66,10 +66,9 @@ object BackgroundImage {
|
||||
data class UncompressedBitmapTooLarge(val width: Long, val height: Long) : FileSizeResult
|
||||
}
|
||||
|
||||
context (AppearanceSettingsFragment)
|
||||
fun validateBackgroundImageFileSize(selectedImage: Uri): FileSizeResult {
|
||||
fun validateBackgroundImageFileSize(target: AppearanceSettingsFragment, selectedImage: Uri): FileSizeResult {
|
||||
val filePathColumn = arrayOf(MediaStore.MediaColumns.SIZE, MediaStore.MediaColumns.WIDTH, MediaStore.MediaColumns.HEIGHT)
|
||||
requireContext().contentResolver.query(selectedImage, filePathColumn, null, null, null).use { cursor ->
|
||||
target.requireContext().contentResolver.query(selectedImage, filePathColumn, null, null, null).use { cursor ->
|
||||
cursor!!.moveToFirst()
|
||||
val fileSizeInMB = cursor.getLong(0) / (1024 * 1024)
|
||||
if (fileSizeInMB >= 10) {
|
||||
@ -88,15 +87,14 @@ object BackgroundImage {
|
||||
}
|
||||
}
|
||||
|
||||
context (AppearanceSettingsFragment)
|
||||
fun import(selectedImage: Uri) {
|
||||
val currentAnkiDroidDirectory = CollectionHelper.getCurrentAnkiDroidDirectory(requireContext())
|
||||
fun import(target: AppearanceSettingsFragment, selectedImage: Uri) {
|
||||
val currentAnkiDroidDirectory = CollectionHelper.getCurrentAnkiDroidDirectory(target.requireContext())
|
||||
val imageName = "DeckPickerBackground.png"
|
||||
val destFile = File(currentAnkiDroidDirectory, imageName)
|
||||
(requireContext().contentResolver.openInputStream(selectedImage) as FileInputStream).channel.use { sourceChannel ->
|
||||
(target.requireContext().contentResolver.openInputStream(selectedImage) as FileInputStream).channel.use { sourceChannel ->
|
||||
FileOutputStream(destFile).channel.use { destChannel ->
|
||||
destChannel.transferFrom(sourceChannel, 0, sourceChannel.size())
|
||||
showSnackbar(R.string.background_image_applied)
|
||||
target.showSnackbar(R.string.background_image_applied)
|
||||
}
|
||||
}
|
||||
this.enabled = true
|
||||
|
@ -149,13 +149,12 @@ class CustomStudyDialog(private val collection: Collection, private val customSt
|
||||
*/
|
||||
val currentDeck = requireArguments().getLong("did")
|
||||
|
||||
val dialogFragment = with(requireContext()) {
|
||||
TagsDialog().withArguments(
|
||||
TagsDialog.DialogType.CUSTOM_STUDY_TAGS,
|
||||
ArrayList(),
|
||||
ArrayList(collection.tags.byDeck(currentDeck))
|
||||
val dialogFragment = TagsDialog().withArguments(
|
||||
context = requireContext(),
|
||||
type = TagsDialog.DialogType.CUSTOM_STUDY_TAGS,
|
||||
checkedTags = ArrayList(),
|
||||
allTags = ArrayList(collection.tags.byDeck(currentDeck))
|
||||
)
|
||||
}
|
||||
customStudyListener?.showDialogFragment(dialogFragment)
|
||||
}
|
||||
else -> {
|
||||
|
@ -107,9 +107,8 @@ class TagsDialog : AnalyticsDialogFragment {
|
||||
* @param allTags all possible tags in the collection
|
||||
* @return Initialized instance of [TagsDialog]
|
||||
*/
|
||||
context(Context)
|
||||
fun withArguments(type: DialogType, checkedTags: List<String>, allTags: List<String>): TagsDialog {
|
||||
return withArguments(type, checkedTags, null, allTags)
|
||||
fun withArguments(context: Context, type: DialogType, checkedTags: List<String>, allTags: List<String>): TagsDialog {
|
||||
return withArguments(context, type, checkedTags, null, allTags)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -121,15 +120,15 @@ class TagsDialog : AnalyticsDialogFragment {
|
||||
* @param allTags all possible tags in the collection
|
||||
* @return Initialized instance of [TagsDialog]
|
||||
*/
|
||||
context(Context)
|
||||
fun withArguments(
|
||||
context: Context,
|
||||
type: DialogType,
|
||||
checkedTags: List<String>,
|
||||
uncheckedTags: List<String>?,
|
||||
allTags: List<String>
|
||||
): TagsDialog {
|
||||
val data = TagsFile.TagsData(type, checkedTags, uncheckedTags, allTags)
|
||||
val file = TagsFile(cacheDir, data)
|
||||
val file = TagsFile(context.cacheDir, data)
|
||||
arguments = this.arguments ?: bundleOf(
|
||||
ARG_TAGS_FILE to file
|
||||
)
|
||||
|
@ -121,7 +121,7 @@ class InstantEditorViewModel : ViewModel(), OnErrorListener {
|
||||
Timber.d("Changing to cloze type note")
|
||||
_currentlySelectedNotetype.postValue(clozeNoteType)
|
||||
Timber.i("Using note type '%d", clozeNoteType.id)
|
||||
editorNote = withCol { Note.fromNotetypeId(clozeNoteType.id) }
|
||||
editorNote = withCol { Note.fromNotetypeId(this@withCol, clozeNoteType.id) }
|
||||
|
||||
_dialogType.emit(DialogType.SHOW_EDITOR_DIALOG)
|
||||
}
|
||||
|
@ -28,15 +28,13 @@ enum class CardsOrNotes {
|
||||
CARDS,
|
||||
NOTES;
|
||||
|
||||
context (Collection)
|
||||
fun saveToCollection() {
|
||||
this@Collection.config.setBool(ConfigKey.Bool.BROWSER_TABLE_SHOW_NOTES_MODE, this == NOTES)
|
||||
fun saveToCollection(col: Collection) {
|
||||
col.config.setBool(ConfigKey.Bool.BROWSER_TABLE_SHOW_NOTES_MODE, this == NOTES)
|
||||
}
|
||||
|
||||
companion object {
|
||||
context (Collection)
|
||||
fun fromCollection(): CardsOrNotes =
|
||||
when (this@Collection.config.getBool(ConfigKey.Bool.BROWSER_TABLE_SHOW_NOTES_MODE)) {
|
||||
fun fromCollection(col: Collection): CardsOrNotes =
|
||||
when (col.config.getBool(ConfigKey.Bool.BROWSER_TABLE_SHOW_NOTES_MODE)) {
|
||||
true -> NOTES
|
||||
false -> CARDS
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ class AppearanceSettingsFragment : SettingsFragment() {
|
||||
}
|
||||
// handling file may result in exception
|
||||
try {
|
||||
when (val sizeResult = BackgroundImage.validateBackgroundImageFileSize(selectedImage)) {
|
||||
when (val sizeResult = BackgroundImage.validateBackgroundImageFileSize(this, selectedImage)) {
|
||||
is FileSizeResult.FileTooLarge -> {
|
||||
showThemedToast(requireContext(), getString(R.string.image_max_size_allowed, sizeResult.maxMB), false)
|
||||
}
|
||||
@ -177,7 +177,7 @@ class AppearanceSettingsFragment : SettingsFragment() {
|
||||
showThemedToast(requireContext(), getString(R.string.image_dimensions_too_large, sizeResult.width, sizeResult.height), false)
|
||||
}
|
||||
is FileSizeResult.OK -> {
|
||||
BackgroundImage.import(selectedImage)
|
||||
BackgroundImage.import(this, selectedImage)
|
||||
}
|
||||
}
|
||||
} catch (e: OutOfMemoryError) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.ichi2.anki.preferences
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.ichi2.anki.CollectionManager.TR
|
||||
import com.ichi2.anki.R
|
||||
import com.ichi2.anki.cardviewer.ViewerCommand
|
||||
@ -77,4 +78,7 @@ class ControlsSettingsFragment : SettingsFragment() {
|
||||
it.title = getString(R.string.gesture_flag_remove).toSentenceCase(R.string.sentence_gesture_flag_remove)
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.toSentenceCase(@StringRes resId: Int): String =
|
||||
this.toSentenceCase(this@ControlsSettingsFragment, resId)
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ class CustomButtonsSettingsFragment : SettingsFragment() {
|
||||
|
||||
private fun setDynamicTitle() {
|
||||
findPreference<Preference>(getString(R.string.custom_button_schedule_card_key))?.let {
|
||||
it.title = TR.actionsSetDueDate().toSentenceCase(R.string.sentence_set_due_date)
|
||||
it.title = TR.actionsSetDueDate().toSentenceCase(this, R.string.sentence_set_due_date)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,6 @@ import com.ichi2.anki.pages.PostRequestHandler
|
||||
import com.ichi2.libanki.Card
|
||||
import com.ichi2.libanki.Sound
|
||||
import com.ichi2.libanki.TtsPlayer
|
||||
import com.ichi2.libanki.note
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@ -223,7 +222,7 @@ abstract class CardViewerViewModel(
|
||||
|
||||
suspend fun getExpectedTypeInAnswer(card: Card, field: JSONObject): String? {
|
||||
val fieldName = field.getString("name")
|
||||
val expected = withCol { card.note().getItem(fieldName) }
|
||||
val expected = withCol { card.note(this@withCol).getItem(fieldName) }
|
||||
return if (fieldName.startsWith("cloze:")) {
|
||||
val clozeIdx = card.ord + 1
|
||||
withCol {
|
||||
|
@ -35,8 +35,6 @@ import com.ichi2.anki.servicelayer.MARKED_TAG
|
||||
import com.ichi2.anki.servicelayer.NoteService
|
||||
import com.ichi2.annotations.NeedsTest
|
||||
import com.ichi2.libanki.Card
|
||||
import com.ichi2.libanki.hasTag
|
||||
import com.ichi2.libanki.note
|
||||
import com.ichi2.libanki.undoableOp
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@ -116,7 +114,7 @@ class PreviewerViewModel(previewerIdsFile: PreviewerIdsFile, firstIndex: Int, ca
|
||||
fun toggleMark() {
|
||||
launchCatchingIO {
|
||||
val card = currentCard.await()
|
||||
val note = withCol { card.note() }
|
||||
val note = withCol { card.note(this@withCol) }
|
||||
NoteService.toggleMark(note)
|
||||
isMarked.emit(NoteService.isMarked(note))
|
||||
}
|
||||
@ -217,7 +215,7 @@ class PreviewerViewModel(previewerIdsFile: PreviewerIdsFile, firstIndex: Int, ca
|
||||
|
||||
private suspend fun updateMarkIcon() {
|
||||
val card = currentCard.await()
|
||||
val isMarkedValue = withCol { card.note().hasTag(MARKED_TAG) }
|
||||
val isMarkedValue = withCol { card.note(this@withCol).hasTag(this@withCol, MARKED_TAG) }
|
||||
isMarked.emit(isMarkedValue)
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ class TemplatePreviewerViewModel(
|
||||
if (arguments.id != 0L) {
|
||||
Note(this, arguments.id)
|
||||
} else {
|
||||
Note.fromNotetypeId(arguments.notetype.id)
|
||||
Note.fromNotetypeId(this@withCol, arguments.notetype.id)
|
||||
}
|
||||
}.apply {
|
||||
fields = arguments.fields
|
||||
|
@ -723,7 +723,7 @@ class CardContentProvider : ContentProvider() {
|
||||
}
|
||||
val fldsArray = Utils.splitFields(flds)
|
||||
// Create empty note
|
||||
val newNote = col.run { Note.fromNotetypeId(thisModelId) }
|
||||
val newNote = Note.fromNotetypeId(col, thisModelId)
|
||||
// Set fields
|
||||
// Check that correct number of flds specified
|
||||
if (fldsArray.size != newNote.fields.size) {
|
||||
@ -766,7 +766,7 @@ class CardContentProvider : ContentProvider() {
|
||||
val tags = values.getAsString(FlashCardsContract.Note.TAGS)
|
||||
// val allowEmpty = AllowEmpty.fromBoolean(values.getAsBoolean(FlashCardsContract.Note.ALLOW_EMPTY))
|
||||
// Create empty note
|
||||
val newNote = col.run { Note.fromNotetypeId(modelId) }
|
||||
val newNote = Note.fromNotetypeId(col, modelId)
|
||||
// Set fields
|
||||
val fldsArray = Utils.splitFields(flds)
|
||||
// Check that correct number of flds specified
|
||||
|
@ -27,8 +27,6 @@ import com.ichi2.anki.CollectionManager.withCol
|
||||
import com.ichi2.anki.R
|
||||
import com.ichi2.libanki.Card
|
||||
import com.ichi2.libanki.Collection
|
||||
import com.ichi2.libanki.timeLimit
|
||||
import com.ichi2.libanki.timeTaken
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
@ -124,10 +122,10 @@ class AnswerTimer(private val cardTimer: Chronometer) {
|
||||
}
|
||||
// Then update and resume the UI timer. Set the base time as if the timer had started
|
||||
// timeTaken() seconds ago.
|
||||
setBase(elapsedRealTime - withCol { currentCard.timeTaken() })
|
||||
setBase(elapsedRealTime - withCol { currentCard.timeTaken(this@withCol) })
|
||||
|
||||
// Don't start the timer if we have already reached the time limit or it will tick over
|
||||
if (elapsedRealTime - cardTimer.base < withCol { currentCard.timeLimit() }) {
|
||||
if (elapsedRealTime - cardTimer.base < withCol { currentCard.timeLimit(this@withCol) }) {
|
||||
cardTimer.start()
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +109,7 @@ class ForgetCardsDialog : DialogFragment() {
|
||||
}
|
||||
|
||||
// this can outlive the lifetime of the fragment
|
||||
private fun AnkiActivity.forgetCards(viewModel: ForgetCardsViewModel) = this@AnkiActivity.launchCatchingTask {
|
||||
private fun AnkiActivity.forgetCards(viewModel: ForgetCardsViewModel) = this.launchCatchingTask {
|
||||
// NICE_TO_HAVE: Display a snackbar if the activity is recreated while this executes
|
||||
val count = withProgress(resources.getString(R.string.dialog_processing)) {
|
||||
// this should be run on the viewModel
|
||||
|
@ -17,6 +17,7 @@
|
||||
package com.ichi2.anki.scheduling
|
||||
|
||||
import android.app.Dialog
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
@ -108,7 +109,7 @@ class SetDueDateDialog : DialogFragment() {
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(requireContext()).create {
|
||||
title(text = CollectionManager.TR.actionsSetDueDate().toSentenceCase(R.string.sentence_set_due_date))
|
||||
title(text = CollectionManager.TR.actionsSetDueDate().toSentenceCase(this@SetDueDateDialog, R.string.sentence_set_due_date))
|
||||
positiveButton(R.string.dialog_ok) { launchUpdateDueDate() }
|
||||
negativeButton(R.string.dialog_cancel)
|
||||
neutralButton(R.string.help)
|
||||
@ -163,7 +164,7 @@ class SetDueDateDialog : DialogFragment() {
|
||||
// The dialog is too wide on tablets
|
||||
// Select either 450dp (tablets)
|
||||
// or 100% of the screen width (smaller phones)
|
||||
val intendedWidth = min(MAX_WIDTH_DP.dpToPx, resources.displayMetrics.widthPixels)
|
||||
val intendedWidth = min(MAX_WIDTH_DP.dpToPx(this.requireContext()), resources.displayMetrics.widthPixels)
|
||||
Timber.d("updating width to %d", intendedWidth)
|
||||
this.dialog?.window?.setLayout(
|
||||
intendedWidth,
|
||||
@ -275,7 +276,7 @@ class SetDueDateDialog : DialogFragment() {
|
||||
}
|
||||
|
||||
// this can outlive the lifetime of the fragment
|
||||
private fun AnkiActivity.updateDueDate(viewModel: SetDueDateViewModel) = this@AnkiActivity.launchCatchingTask {
|
||||
private fun AnkiActivity.updateDueDate(viewModel: SetDueDateViewModel) = this.launchCatchingTask {
|
||||
// NICE_TO_HAVE: Display a snackbar if the activity is recreated while this executes
|
||||
val cardsUpdated = withProgress(resources.getString(R.string.dialog_processing)) {
|
||||
// this is async as it should be run on the viewModel
|
||||
@ -285,12 +286,12 @@ private fun AnkiActivity.updateDueDate(viewModel: SetDueDateViewModel) = this@An
|
||||
|
||||
if (cardsUpdated == null) {
|
||||
Timber.w("unable to update due date")
|
||||
showThemedToast(R.string.something_wrong, true)
|
||||
showThemedToast(this@updateDueDate, R.string.something_wrong, true)
|
||||
return@launchCatchingTask
|
||||
}
|
||||
showSnackbar(TR.schedulingSetDueDateDone(cardsUpdated), Snackbar.LENGTH_SHORT)
|
||||
}
|
||||
|
||||
context (DialogFragment)
|
||||
val Int.dpToPx: Int
|
||||
get() = (this * requireContext().resources.displayMetrics.density + 0.5f).toInt()
|
||||
// TODO: See if we can turn this to a `val` when context parameters are back
|
||||
fun Int.dpToPx(context: Context): Int =
|
||||
(this * context.resources.displayMetrics.density + 0.5f).toInt()
|
||||
|
@ -26,10 +26,9 @@ object CardService {
|
||||
* @param selectedCardIds list of card ids
|
||||
* can do better with performance here
|
||||
*/
|
||||
context (Collection)
|
||||
fun selectedNoteIds(selectedCardIds: List<CardId>) =
|
||||
fun selectedNoteIds(col: Collection, selectedCardIds: List<CardId>) =
|
||||
CardUtils.getNotes(
|
||||
this@Collection,
|
||||
selectedCardIds.map { this@Collection.getCard(it) }
|
||||
col,
|
||||
selectedCardIds.map { col.getCard(it) }
|
||||
).map { it.id }
|
||||
}
|
||||
|
@ -78,8 +78,12 @@ object NoteService {
|
||||
return null
|
||||
}
|
||||
|
||||
context (Collection)
|
||||
fun updateMultimediaNoteFromFields(fields: Array<String>, modelId: NoteTypeId, mmNote: MultimediaEditableNote) {
|
||||
fun updateMultimediaNoteFromFields(
|
||||
col: Collection,
|
||||
fields: Array<String>,
|
||||
modelId: NoteTypeId,
|
||||
mmNote: MultimediaEditableNote
|
||||
) {
|
||||
for (i in fields.indices) {
|
||||
val value = fields[i]
|
||||
val field: IField = if (value.startsWith("<img")) {
|
||||
@ -91,7 +95,7 @@ object NoteService {
|
||||
} else {
|
||||
TextField()
|
||||
}
|
||||
field.setFormattedString(this@Collection, value)
|
||||
field.setFormattedString(col, value)
|
||||
mmNote.setField(i, field)
|
||||
}
|
||||
mmNote.modelId = modelId
|
||||
@ -118,10 +122,6 @@ object NoteService {
|
||||
}
|
||||
}
|
||||
|
||||
context (Collection)
|
||||
fun importMediaFileToDirectory(field: IField?) =
|
||||
importMediaToDirectory(this@Collection, field)
|
||||
|
||||
/**
|
||||
* Considering the field is new, if it has media handle it
|
||||
*
|
||||
|
@ -29,17 +29,15 @@ import androidx.fragment.app.Fragment
|
||||
* "Toggle Suspend".toSentenceCase(R.string.sentence_toggle_suspend) // "Toggle suspend"
|
||||
* ```
|
||||
*/
|
||||
context (Activity)
|
||||
fun String.toSentenceCase(@StringRes resId: Int): String {
|
||||
val resString = getString(resId)
|
||||
fun String.toSentenceCase(activity: Activity, @StringRes resId: Int): String {
|
||||
val resString = activity.getString(resId)
|
||||
// lowercase both for the comparison: sentence case doesn't mean all words are lowercase
|
||||
if (this.lowercase() == resString.lowercase()) return resString
|
||||
return this
|
||||
}
|
||||
|
||||
context (Fragment)
|
||||
fun String.toSentenceCase(@StringRes resId: Int): String {
|
||||
val resString = getString(resId)
|
||||
fun String.toSentenceCase(fragment: Fragment, @StringRes resId: Int): String {
|
||||
val resString = fragment.getString(resId)
|
||||
// lowercase both for the comparison: sentence case doesn't mean all words are lowercase
|
||||
if (this.lowercase() == resString.lowercase()) return resString
|
||||
return this
|
||||
|
@ -95,7 +95,7 @@ class ManageSpaceViewModel(val app: Application) : AndroidViewModel(app), Collec
|
||||
|
||||
suspend fun deleteMediaFiles(filesNamesToDelete: List<String>) {
|
||||
try {
|
||||
withCol { deleteMedia(filesNamesToDelete) }
|
||||
withCol { deleteMedia(this@withCol, filesNamesToDelete) }
|
||||
} finally {
|
||||
launchCalculationOfSizeOfEverything()
|
||||
launchCalculationOfCollectionSize()
|
||||
@ -117,7 +117,7 @@ class ManageSpaceViewModel(val app: Application) : AndroidViewModel(app), Collec
|
||||
|
||||
suspend fun deleteBackups(backupsToDelete: List<File>) {
|
||||
try {
|
||||
withCol { BackupManager.deleteBackups(backupsToDelete) }
|
||||
withCol { BackupManager.deleteBackups(this@withCol, backupsToDelete) }
|
||||
} finally {
|
||||
launchCalculationOfBackupsSize()
|
||||
launchCalculationOfCollectionSize()
|
||||
|
@ -43,8 +43,6 @@ import com.ichi2.anki.servicelayer.isBuryNoteAvailable
|
||||
import com.ichi2.anki.servicelayer.isSuspendNoteAvailable
|
||||
import com.ichi2.anki.ui.windows.reviewer.autoadvance.AutoAdvance
|
||||
import com.ichi2.libanki.ChangeManager
|
||||
import com.ichi2.libanki.hasTag
|
||||
import com.ichi2.libanki.note
|
||||
import com.ichi2.libanki.redo
|
||||
import com.ichi2.libanki.sched.Counts
|
||||
import com.ichi2.libanki.sched.CurrentQueueState
|
||||
@ -161,7 +159,7 @@ class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) :
|
||||
fun toggleMark() {
|
||||
launchCatchingIO {
|
||||
val card = currentCard.await()
|
||||
val note = withCol { card.note() }
|
||||
val note = withCol { card.note(this@withCol) }
|
||||
NoteService.toggleMark(note)
|
||||
isMarkedFlow.emit(NoteService.isMarked(note))
|
||||
}
|
||||
@ -371,7 +369,7 @@ class ReviewerViewModel(cardMediaPlayer: CardMediaPlayer) :
|
||||
|
||||
private suspend fun updateMarkedStatus() {
|
||||
val card = currentCard.await()
|
||||
val isMarkedValue = withCol { card.note().hasTag(MARKED_TAG) }
|
||||
val isMarkedValue = withCol { card.note(this@withCol).hasTag(this@withCol, MARKED_TAG) }
|
||||
isMarkedFlow.emit(isMarkedValue)
|
||||
}
|
||||
|
||||
|
@ -32,20 +32,18 @@ import timber.log.Timber
|
||||
* Takes a list of media file names and removes them from the [Collection]
|
||||
* @param unused List of media names to be deleted
|
||||
*/
|
||||
context (Collection)
|
||||
fun deleteMedia(unused: List<String>): Int {
|
||||
fun deleteMedia(col: Collection, unused: List<String>): Int {
|
||||
// FIXME: this provides progress info that is not currently used
|
||||
this@Collection.media.removeFiles(unused)
|
||||
col.media.removeFiles(unused)
|
||||
return unused.size
|
||||
}
|
||||
|
||||
// TODO: Once [com.ichi2.async.CollectionTask.RebuildCram] and [com.ichi2.async.CollectionTask.EmptyCram]
|
||||
// are migrated to Coroutines, move this function to [com.ichi2.anki.StudyOptionsFragment]
|
||||
context (Collection)
|
||||
fun updateValuesFromDeck(): StudyOptionsFragment.DeckStudyData? {
|
||||
fun updateValuesFromDeck(col: Collection): StudyOptionsFragment.DeckStudyData? {
|
||||
Timber.d("doInBackgroundUpdateValuesFromDeck")
|
||||
return try {
|
||||
val sched = this@Collection.sched
|
||||
val sched = col.sched
|
||||
val counts = sched.counts()
|
||||
val totalNewCount = sched.totalNewForCurrentDeck()
|
||||
val totalCount = sched.cardCount()
|
||||
@ -117,13 +115,13 @@ suspend fun renderBrowserQA(
|
||||
* Handles everything for a model change at once - template add / deletes as well as content updates
|
||||
* @return Pair<Boolean, String> : (true, null) when success, (false, exceptionMessage) when failure
|
||||
*/
|
||||
context (Collection)
|
||||
fun saveModel(
|
||||
col: Collection,
|
||||
notetype: NotetypeJson,
|
||||
templateChanges: ArrayList<Array<Any>>
|
||||
) {
|
||||
Timber.d("doInBackgroundSaveModel")
|
||||
val oldModel = this@Collection.notetypes.get(notetype.getLong("id"))
|
||||
val oldModel = col.notetypes.get(notetype.getLong("id"))
|
||||
|
||||
// TODO: make undoable
|
||||
val newTemplates = notetype.getJSONArray("tmpls")
|
||||
@ -132,11 +130,11 @@ fun saveModel(
|
||||
when (change[1] as CardTemplateNotetype.ChangeType) {
|
||||
CardTemplateNotetype.ChangeType.ADD -> {
|
||||
Timber.d("doInBackgroundSaveModel() adding template %s", change[0])
|
||||
this@Collection.notetypes.addTemplate(oldModel, newTemplates.getJSONObject(change[0] as Int))
|
||||
col.notetypes.addTemplate(oldModel, newTemplates.getJSONObject(change[0] as Int))
|
||||
}
|
||||
CardTemplateNotetype.ChangeType.DELETE -> {
|
||||
Timber.d("doInBackgroundSaveModel() deleting template currently at ordinal %s", change[0])
|
||||
this@Collection.notetypes.remTemplate(oldModel, oldTemplates.getJSONObject(change[0] as Int))
|
||||
col.notetypes.remTemplate(oldModel, oldTemplates.getJSONObject(change[0] as Int))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -144,6 +142,6 @@ fun saveModel(
|
||||
// required for Rust: the modified time can't go backwards, and we updated the model by adding fields
|
||||
// This could be done better
|
||||
notetype.put("mod", oldModel!!.getLong("mod"))
|
||||
this@Collection.notetypes.save(notetype)
|
||||
this@Collection.notetypes.update(notetype)
|
||||
col.notetypes.save(notetype)
|
||||
col.notetypes.update(notetype)
|
||||
}
|
||||
|
@ -229,6 +229,3 @@ interface Compat {
|
||||
@Suppress("PropertyName")
|
||||
val AXIS_GESTURE_SCROLL_Y_DISTANCE: Int
|
||||
}
|
||||
|
||||
context (Context)
|
||||
fun Compat.vibrate(duration: Duration) = vibrate(this@Context, duration)
|
||||
|
@ -463,23 +463,3 @@ open class Card : Cloneable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @see Card.renderOutput */
|
||||
context (Collection)
|
||||
fun Card.renderOutput(reload: Boolean = false, browser: Boolean = false) =
|
||||
this@Card.renderOutput(this@Collection, reload, browser)
|
||||
|
||||
/** @see Card.note */
|
||||
context (Collection)
|
||||
fun Card.note() =
|
||||
this@Card.note(this@Collection)
|
||||
|
||||
/** @see Card.timeTaken */
|
||||
context (Collection)
|
||||
fun Card.timeTaken() =
|
||||
this@Card.timeTaken(this@Collection)
|
||||
|
||||
/** @see Card.timeLimit */
|
||||
context (Collection)
|
||||
fun Card.timeLimit() =
|
||||
this@Card.timeLimit(this@Collection)
|
||||
|
@ -310,7 +310,7 @@ class Collection(
|
||||
* @return The new note
|
||||
*/
|
||||
fun newNote(notetype: NotetypeJson): Note {
|
||||
return Note.fromNotetypeId(notetype.id)
|
||||
return Note.fromNotetypeId(this, notetype.id)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -65,10 +65,9 @@ class Note : Cloneable {
|
||||
}
|
||||
|
||||
companion object {
|
||||
context (Collection)
|
||||
fun fromNotetypeId(ntid: NoteTypeId): Note {
|
||||
val backendNote = backend.newNote(ntid)
|
||||
return Note(this@Collection, backendNote)
|
||||
fun fromNotetypeId(col: Collection, ntid: NoteTypeId): Note {
|
||||
val backendNote = col.backend.newNote(ntid)
|
||||
return Note(col, backendNote)
|
||||
}
|
||||
}
|
||||
|
||||
@ -301,15 +300,3 @@ class Note : Cloneable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @see Note.hasTag */
|
||||
context (Collection)
|
||||
fun Note.hasTag(tag: String) = this.hasTag(this@Collection, tag)
|
||||
|
||||
/** @see Note.setTagsFromStr */
|
||||
context (Collection)
|
||||
fun Note.setTagsFromStr(str: String) = this.setTagsFromStr(this@Collection, str)
|
||||
|
||||
/** @see Note.load */
|
||||
context (Collection)
|
||||
fun Note.load() = this.load(this@Collection)
|
||||
|
@ -63,13 +63,13 @@ class CardBrowserColumnTest : JvmTest() {
|
||||
|
||||
@Test
|
||||
fun `cards - ensure old values match backend values`() {
|
||||
with(col) { CardsOrNotes.CARDS.saveToCollection() }
|
||||
CardsOrNotes.CARDS.saveToCollection(col)
|
||||
`ensure old values match backend values`(CardsOrNotes.CARDS)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `notes - ensure old values match backend values`() {
|
||||
with(col) { CardsOrNotes.NOTES.saveToCollection() }
|
||||
CardsOrNotes.NOTES.saveToCollection(col)
|
||||
`ensure old values match backend values`(CardsOrNotes.NOTES)
|
||||
}
|
||||
|
||||
|
@ -247,7 +247,7 @@ class CardBrowserViewModelTest : JvmTest() {
|
||||
fun `sort order from notes is selected - 16514`() {
|
||||
col.config.set("sortType", "noteCrt")
|
||||
col.config.set("noteSortType", "_field_Frequency")
|
||||
with(col) { CardsOrNotes.NOTES.saveToCollection() }
|
||||
CardsOrNotes.NOTES.saveToCollection(col)
|
||||
|
||||
runViewModelTest(notes = 1) {
|
||||
assertThat("1 row returned", rowCount, equalTo(1))
|
||||
@ -716,7 +716,7 @@ class CardBrowserViewModelTest : JvmTest() {
|
||||
testBody: suspend CardBrowserViewModel.() -> Unit
|
||||
) =
|
||||
runTest {
|
||||
with(col) { CardsOrNotes.NOTES.saveToCollection() }
|
||||
CardsOrNotes.NOTES.saveToCollection(col)
|
||||
for (i in 0 until notes) {
|
||||
// ensure 1 note = 2 cards
|
||||
addNoteUsingBasicAndReversedModel()
|
||||
@ -770,7 +770,7 @@ class CardBrowserViewModelTest : JvmTest() {
|
||||
|
||||
// default is CARDS, do nothing in this case
|
||||
if (mode == CardsOrNotes.NOTES) {
|
||||
CollectionManager.withCol { mode.saveToCollection() }
|
||||
CollectionManager.withCol { mode.saveToCollection(this@withCol) }
|
||||
}
|
||||
|
||||
val cache = File(createTempDirectory().pathString)
|
||||
|
@ -68,5 +68,5 @@ class CardSoundConfigTest : JvmTest() {
|
||||
fun `cards with the same deck options are equal`() {
|
||||
}
|
||||
|
||||
private suspend fun createCardSoundConfig(card: Card) = withCol { CardSoundConfig.create(card) }
|
||||
private suspend fun createCardSoundConfig(card: Card) = withCol { CardSoundConfig.create(this@withCol, card) }
|
||||
}
|
||||
|
@ -688,25 +688,25 @@ class TagsDialogTest : RobolectricTest() {
|
||||
type: TagsDialog.DialogType,
|
||||
checkedTags: List<String>,
|
||||
allTags: List<String>
|
||||
) =
|
||||
with(this@TagsDialogTest.targetContext) {
|
||||
withArguments(type = type, checkedTags = checkedTags, allTags = allTags)
|
||||
}
|
||||
) = withArguments(
|
||||
context = targetContext,
|
||||
type = type,
|
||||
checkedTags = checkedTags,
|
||||
allTags = allTags
|
||||
)
|
||||
|
||||
private fun TagsDialog.withTestArguments(
|
||||
type: TagsDialog.DialogType,
|
||||
checkedTags: List<String>,
|
||||
uncheckedTags: List<String>?,
|
||||
allTags: List<String>
|
||||
) =
|
||||
with(this@TagsDialogTest.targetContext) {
|
||||
withArguments(
|
||||
) = withArguments(
|
||||
context = targetContext,
|
||||
type = type,
|
||||
checkedTags = checkedTags,
|
||||
uncheckedTags = uncheckedTags,
|
||||
allTags = allTags
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private fun mockLifecycleOwner(): LifecycleOwner {
|
||||
|
@ -216,8 +216,7 @@ class InstantEditorViewModelTest : RobolectricTest() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
context (TestClass)
|
||||
fun runInstantEditorViewModelTest(
|
||||
fun TestClass.runInstantEditorViewModelTest(
|
||||
initViewModel: () -> InstantEditorViewModel = { InstantEditorViewModel() },
|
||||
testBody: suspend InstantEditorViewModel.() -> Unit
|
||||
) = runTest {
|
||||
@ -227,23 +226,20 @@ class InstantEditorViewModelTest : RobolectricTest() {
|
||||
}
|
||||
}
|
||||
|
||||
context (InstantEditorViewModel)
|
||||
private fun toggleAllClozeDeletions(words: MutableList<String>) {
|
||||
private fun InstantEditorViewModel.toggleAllClozeDeletions(words: MutableList<String>) {
|
||||
for (index in words.indices) {
|
||||
words[index] = buildClozeText(words[index])
|
||||
}
|
||||
}
|
||||
|
||||
context (InstantEditorViewModel)
|
||||
@Suppress("SameParameterValue")
|
||||
private fun toggleClozeDeletions(words: MutableList<String>, vararg indices: Int) {
|
||||
private fun InstantEditorViewModel.toggleClozeDeletions(words: MutableList<String>, vararg indices: Int) {
|
||||
for (index in indices) {
|
||||
words[index] = buildClozeText(words[index])
|
||||
}
|
||||
}
|
||||
|
||||
context (InstantEditorViewModel)
|
||||
@Suppress("SameParameterValue")
|
||||
private fun toggleClozeDeletion(words: MutableList<String>, index: Int) {
|
||||
private fun InstantEditorViewModel.toggleClozeDeletion(words: MutableList<String>, index: Int) {
|
||||
words[index] = buildClozeText(words[index])
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ class NoteServiceTest : RobolectricTest() {
|
||||
multiMediaNote!!.getField(0)!!.text = "foo"
|
||||
multiMediaNote.getField(1)!!.text = "bar"
|
||||
|
||||
val basicNote = col.run { Note.fromNotetypeId(testModel.id) }.apply {
|
||||
val basicNote = Note.fromNotetypeId(col, testModel.id).apply {
|
||||
setField(0, "this should be changed to foo")
|
||||
setField(1, "this should be changed to bar")
|
||||
}
|
||||
@ -81,7 +81,7 @@ class NoteServiceTest : RobolectricTest() {
|
||||
testNotetype = col.notetypes.newBasicNotetype()
|
||||
testNotetype.id = 45
|
||||
col.notetypes.add(testNotetype)
|
||||
val noteWithID45 = col.run { Note.fromNotetypeId(testNotetype.id) }
|
||||
val noteWithID45 = Note.fromNotetypeId(col, testNotetype.id)
|
||||
val expectedException: Throwable = assertThrows(RuntimeException::class.java) { NoteService.updateJsonNoteFromMultimediaNote(multiMediaNoteWithID42, noteWithID45) }
|
||||
assertEquals(expectedException.message, "Source and Destination Note ID do not match.")
|
||||
}
|
||||
|
@ -34,12 +34,12 @@ class SentenceCaseTest : RobolectricTest() {
|
||||
@Test
|
||||
fun `English is converted to sentence case`() {
|
||||
with(super.startRegularActivity<IntroductionActivity>()) {
|
||||
assertThat(TR.browsingToggleSuspend().toSentenceCase(R.string.sentence_toggle_suspend), equalTo("Toggle suspend"))
|
||||
assertThat(TR.browsingToggleBury().toSentenceCase(R.string.sentence_toggle_bury), equalTo("Toggle bury"))
|
||||
assertThat(TR.actionsSetDueDate().toSentenceCase(R.string.sentence_set_due_date), equalTo("Set due date"))
|
||||
assertThat(TR.browsingToggleSuspend().toSentenceCase(this, R.string.sentence_toggle_suspend), equalTo("Toggle suspend"))
|
||||
assertThat(TR.browsingToggleBury().toSentenceCase(this, R.string.sentence_toggle_bury), equalTo("Toggle bury"))
|
||||
assertThat(TR.actionsSetDueDate().toSentenceCase(this, R.string.sentence_set_due_date), equalTo("Set due date"))
|
||||
|
||||
assertThat("Toggle Suspend".toSentenceCase(R.string.sentence_toggle_suspend), equalTo("Toggle suspend"))
|
||||
assertThat("Ook? Ook?".toSentenceCase(R.string.sentence_toggle_suspend), equalTo("Ook? Ook?"))
|
||||
assertThat("Toggle Suspend".toSentenceCase(this, R.string.sentence_toggle_suspend), equalTo("Toggle suspend"))
|
||||
assertThat("Ook? Ook?".toSentenceCase(this, R.string.sentence_toggle_suspend), equalTo("Ook? Ook?"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,8 +63,7 @@ class OnlyOnceTest : RobolectricTest() {
|
||||
}
|
||||
|
||||
// catch the exception here otherwise the test scope will catch it and throw it, safe as we expect the exception
|
||||
context(TestScope)
|
||||
private fun preventMultipleExecutions(
|
||||
private fun TestScope.preventMultipleExecutions(
|
||||
shouldCatchException: Boolean = false,
|
||||
wait: Boolean,
|
||||
function: () -> Unit
|
||||
@ -78,6 +77,6 @@ class OnlyOnceTest : RobolectricTest() {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (wait) this@TestScope.advanceUntilIdle()
|
||||
if (wait) advanceUntilIdle()
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ subprojects {
|
||||
tasks.withType(KotlinCompile::class.java).configureEach {
|
||||
compilerOptions {
|
||||
allWarningsAsErrors = fatalWarnings
|
||||
val compilerArgs = mutableListOf("-Xjvm-default=all", "-Xcontext-receivers")
|
||||
val compilerArgs = mutableListOf("-Xjvm-default=all")
|
||||
if (project.name != "api") {
|
||||
compilerArgs += "-opt-in=kotlinx.coroutines.ExperimentalCoroutinesApi"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user