diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/NoteEditorTabOrderTest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/NoteEditorTabOrderTest.kt index a466410f80f0..d906fce97313 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/NoteEditorTabOrderTest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/NoteEditorTabOrderTest.kt @@ -39,9 +39,9 @@ class NoteEditorTabOrderTest : NoteEditorTest() { @Ignore( """flaky on API 21 as well: com.ichi2.anki.NoteEditorTabOrderTest > testTabOrder[test(AVD) - 5.1.1] FAILED - java.lang.AssertionError: + java.lang.AssertionError: - Expected: is "a"""", + Expected: is "a"""", ) @Throws(Throwable::class) fun testTabOrder() { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Info.kt b/AnkiDroid/src/main/java/com/ichi2/anki/Info.kt index 8133c65df0da..7c3e00814c25 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/Info.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/Info.kt @@ -138,12 +138,17 @@ class Info : */ @Suppress("ktlint:standard:max-line-length") webView.loadUrl( - "javascript:document.body.style.setProperty(\"color\", \"" + textColor + "\");" + - "x=document.getElementsByTagName(\"a\"); for(i=0;i 0) { if (columnCount < 7) { - metaDb.execSQL("ALTER TABLE widgetStatus " + "ADD COLUMN eta INTEGER NOT NULL DEFAULT '0'") - metaDb.execSQL("ALTER TABLE widgetStatus " + "ADD COLUMN time INTEGER NOT NULL DEFAULT '0'") + metaDb.execSQL("ALTER TABLE widgetStatus ADD COLUMN eta INTEGER NOT NULL DEFAULT '0'") + metaDb.execSQL("ALTER TABLE widgetStatus ADD COLUMN time INTEGER NOT NULL DEFAULT '0'") } } else { metaDb.execSQL( - "CREATE TABLE IF NOT EXISTS widgetStatus (" + "deckId INTEGER NOT NULL PRIMARY KEY, " + - "deckName TEXT NOT NULL, " + "newCards INTEGER NOT NULL, " + "lrnCards INTEGER NOT NULL, " + - "dueCards INTEGER NOT NULL, " + "progress INTEGER NOT NULL, " + "eta INTEGER NOT NULL)", + """CREATE TABLE IF NOT EXISTS widgetStatus ( + deckId INTEGER NOT NULL PRIMARY KEY, + deckName TEXT NOT NULL, + newCards INTEGER NOT NULL, + lrnCards INTEGER NOT NULL, + dueCards INTEGER NOT NULL, + progress INTEGER NOT NULL, + eta INTEGER NOT NULL + )""", ) } } @@ -225,7 +248,7 @@ object MetaDB { try { if ("" == getLanguage(context, did, ord, qa)) { mMetaDb!!.execSQL( - "INSERT INTO languages (did, ord, qa, language) " + " VALUES (?, ?, ?, ?);", + "INSERT INTO languages (did, ord, qa, language) VALUES (?, ?, ?, ?);", arrayOf( did, ord, @@ -520,9 +543,7 @@ object MetaDB { ).use { cur -> if (cur.moveToNext()) { metaDb.execSQL( - "UPDATE whiteboardState SET did = ?, " + - columnName + "= ? " + - " WHERE _id=?;", + "UPDATE whiteboardState SET did = ?, $columnName= ? WHERE _id=?;", arrayOf(did, value, cur.getString(0)), ) } else { diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt index 0fa8b2a880f7..d51534bc89dc 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt @@ -99,7 +99,8 @@ import com.ichi2.annotations.NeedsTest import com.ichi2.libanki.Card import com.ichi2.libanki.CardId import com.ichi2.libanki.Collection -import com.ichi2.libanki.Consts +import com.ichi2.libanki.Consts.QUEUE_TYPE_NEW +import com.ichi2.libanki.Consts.QUEUE_TYPE_SUSPENDED import com.ichi2.libanki.sched.Counts import com.ichi2.libanki.sched.CurrentQueueState import com.ichi2.libanki.undoableOp @@ -1617,7 +1618,7 @@ open class Reviewer : false } else { getColUnsafe.db.queryScalar( - "select 1 from cards where nid = ? and id != ? and queue != " + Consts.QUEUE_TYPE_SUSPENDED + " limit 1", + "select 1 from cards where nid = ? and id != ? and queue != $QUEUE_TYPE_SUSPENDED limit 1", currentCard!!.nid, currentCard!!.id, ) == 1 @@ -1631,7 +1632,7 @@ open class Reviewer : false } else { getColUnsafe.db.queryScalar( - "select 1 from cards where nid = ? and id != ? and queue >= " + Consts.QUEUE_TYPE_NEW + " limit 1", + "select 1 from cards where nid = ? and id != ? and queue >= $QUEUE_TYPE_NEW limit 1", currentCard!!.nid, currentCard!!.id, ) == 1 diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt index 4a04103e01a1..633333c5230e 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/customstudy/CustomStudyDialog.kt @@ -297,8 +297,7 @@ class CustomStudyDialog( createCustomStudySession( JSONArray(), arrayOf( - "is:new added:" + - n, + "is:new added:$n", Consts.DYN_MAX_SIZE, Consts.DYN_OLDEST, ), @@ -489,7 +488,7 @@ class CustomStudyDialog( dyn.put("delays", JSONObject.NULL) } val ar = dyn.getJSONArray("terms") - ar.getJSONArray(0).put(0, "deck:\"" + deckToStudyName + "\" " + terms[0]) + ar.getJSONArray(0).put(0, """deck:"$deckToStudyName" terms[0]""") ar.getJSONArray(0).put(1, terms[1]) @DynPriority val priority = terms[2] as Int ar.getJSONArray(0).put(2, priority) diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.kt index 1bf5fcebfb15..7905d6b4ef4e 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.kt +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/Collection.kt @@ -613,8 +613,11 @@ class Collection( } val badNotes = db.queryScalar( - "select 1 from notes where id not in (select distinct nid from cards) " + - "or mid not in " + ids2str(notetypes.ids()) + " limit 1", + """select 1 from notes where + id not in (select distinct nid from cards) + or + mid not in ${ids2str(notetypes.ids())} + limit 1""", ) > 0 // notes without cards or models if (badNotes) { @@ -630,8 +633,11 @@ class Collection( val tmpls = m.getJSONArray("tmpls") val badOrd = db.queryScalar( - "select 1 from cards where (ord < 0 or ord >= ?) and nid in ( " + - "select id from notes where mid = ?) limit 1", + """select 1 from cards where + (ord < 0 or ord >= ?) + and + nid in (select id from notes where mid = ?) + limit 1""", tmpls.length(), m.getLong("id"), ) > 0 @@ -650,10 +656,7 @@ class Collection( cids: List, ) { db.execute( - "update cards set flags = (flags & ~?) | ?, usn=?, mod=? where id in " + - ids2str( - cids, - ), + "update cards set flags = (flags & ~?) | ?, usn=?, mod=? where id in ${ids2str(cids)}", 7, flag.code, usn(), diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/Notetypes.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/Notetypes.kt index cf7ce87d5dcc..57ab9fbb8e26 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/Notetypes.kt +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/Notetypes.kt @@ -58,6 +58,7 @@ import com.ichi2.utils.HashUtil import com.ichi2.utils.jsonObjectIterable import net.ankiweb.rsdroid.RustCleanup import net.ankiweb.rsdroid.exceptions.BackendNotFoundException +import org.intellij.lang.annotations.Language import org.json.JSONArray import org.json.JSONObject import timber.log.Timber @@ -676,7 +677,7 @@ class Notetypes( ords: IntArray, ): List? { val cardIdsToDeleteSql = - "select c2.id from cards c2, notes n2 where c2.nid=n2.id and n2.mid = ? and c2.ord in " + Utils.ids2str(ords) + "select c2.id from cards c2, notes n2 where c2.nid=n2.id and n2.mid = ? and c2.ord in ${Utils.ids2str(ords)}" val cids: List = col.db.queryLongList(cardIdsToDeleteSql, modelId) // Timber.d("cardIdsToDeleteSql was ' %s' and got %s", cardIdsToDeleteSql, Utils.ids2str(cids)); Timber.d("getCardIdsForModel found %s cards to delete for model %s and ords %s", cids.size, modelId, Utils.ids2str(ords)) @@ -687,7 +688,7 @@ class Notetypes( Timber.d("noteCountPreDeleteSql was '%s'", noteCountPreDeleteSql) Timber.d("preDeleteNoteCount is %s", preDeleteNoteCount) val noteCountPostDeleteSql = - "select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?) and ord not in " + Utils.ids2str(ords) + "select count(distinct(nid)) from cards where nid in (select id from notes where mid = ?) and ord not in ${Utils.ids2str(ords)}" Timber.d("noteCountPostDeleteSql was '%s'", noteCountPostDeleteSql) val postDeleteNoteCount: Int = col.db.queryScalar(noteCountPostDeleteSql, modelId) Timber.d("postDeleteNoteCount would be %s", postDeleteNoteCount) @@ -708,12 +709,9 @@ class Notetypes( it.put("name", name) } + @Language("JSON") private const val DEFAULT_TEMPLATE = - ( - "{\"name\": \"\", " + "\"ord\": null, " + "\"qfmt\": \"\", " + - "\"afmt\": \"\", " + "\"did\": null, " + "\"bqfmt\": \"\"," + "\"bafmt\": \"\"," + "\"bfont\": \"\"," + - "\"bsize\": 0 }" - ) + """{"name": "", "ord": null, "qfmt": "", "afmt": "", "did": null, "bqfmt": "","bafmt": "","bfont": "", "bsize": 0 }""" /** "Mapping of field name -> (ord, field). */ fun fieldMap(notetype: NotetypeJson): Map> { diff --git a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Scheduler.kt b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Scheduler.kt index ef0541045698..d6fec17b4440 100644 --- a/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Scheduler.kt +++ b/AnkiDroid/src/main/java/com/ichi2/libanki/sched/Scheduler.kt @@ -45,7 +45,12 @@ import com.ichi2.anki.utils.SECONDS_PER_DAY import com.ichi2.libanki.Card import com.ichi2.libanki.CardId import com.ichi2.libanki.Collection -import com.ichi2.libanki.Consts +import com.ichi2.libanki.Consts.CARD_TYPE_LRN +import com.ichi2.libanki.Consts.CARD_TYPE_NEW +import com.ichi2.libanki.Consts.CARD_TYPE_RELEARNING +import com.ichi2.libanki.Consts.CARD_TYPE_REV +import com.ichi2.libanki.Consts.QUEUE_TYPE_NEW +import com.ichi2.libanki.Consts.QUEUE_TYPE_REV import com.ichi2.libanki.DeckConfig import com.ichi2.libanki.DeckId import com.ichi2.libanki.EpochSeconds @@ -490,9 +495,7 @@ open class Scheduler( @Suppress("ktlint:standard:max-line-length") fun totalNewForCurrentDeck(): Int = col.db.queryScalar( - "SELECT count() FROM cards WHERE id IN (SELECT id FROM cards WHERE did IN " + deckLimit() + " AND queue = " + - Consts.QUEUE_TYPE_NEW + - " LIMIT ?)", + "SELECT count() FROM cards WHERE id IN (SELECT id FROM cards WHERE did IN ${deckLimit()} AND queue = $QUEUE_TYPE_NEW LIMIT ?)", REPORT_LIMIT, ) @@ -501,9 +504,7 @@ open class Scheduler( @Suppress("ktlint:standard:max-line-length") fun totalRevForCurrentDeck(): Int = col.db.queryScalar( - "SELECT count() FROM cards WHERE id IN (SELECT id FROM cards WHERE did IN " + deckLimit() + " AND queue = " + - Consts.QUEUE_TYPE_REV + - " AND due <= ? LIMIT ?)", + "SELECT count() FROM cards WHERE id IN (SELECT id FROM cards WHERE did IN ${deckLimit()} AND queue = $QUEUE_TYPE_REV AND due <= ? LIMIT ?)", today, REPORT_LIMIT, ) @@ -526,18 +527,17 @@ open class Scheduler( /** true if there are any rev cards due. */ @Suppress("ktlint:standard:max-line-length") - open fun revDue(): Boolean = + open fun revDue() = col.db .queryScalar( - "SELECT 1 FROM cards WHERE did IN " + deckLimit() + " AND queue = " + Consts.QUEUE_TYPE_REV + " AND due <= ?" + - " LIMIT 1", + """SELECT 1 FROM cards WHERE did IN ${deckLimit()} AND queue = $QUEUE_TYPE_REV AND due <= ? LIMIT 1""", today, ) != 0 /** true if there are any new cards due. */ @Suppress("ktlint:standard:max-line-length") open fun newDue(): Boolean = - col.db.queryScalar("SELECT 1 FROM cards WHERE did IN " + deckLimit() + " AND queue = " + Consts.QUEUE_TYPE_NEW + " LIMIT 1") != + col.db.queryScalar("SELECT 1 FROM cards WHERE did IN ${deckLimit()} AND queue = $QUEUE_TYPE_NEW LIMIT 1") != 0 private val etaCache: DoubleArray = doubleArrayOf(-1.0, -1.0, -1.0, -1.0, -1.0, -1.0) @@ -574,23 +574,14 @@ open class Scheduler( col .db .query( - "select " + - "avg(case when type = " + Consts.CARD_TYPE_NEW + - " then case when ease > 1 then 1.0 else 0.0 end else null end) as newRate, avg(case when type = " + - Consts.CARD_TYPE_NEW + - " then time else null end) as newTime, " + - "avg(case when type in (" + Consts.CARD_TYPE_LRN + ", " + Consts.CARD_TYPE_RELEARNING + - ") then case when ease > 1 then 1.0 else 0.0 end else null end) as revRate, avg(case when type in (" + - Consts.CARD_TYPE_LRN + - ", " + - Consts.CARD_TYPE_RELEARNING + - ") then time else null end) as revTime, " + - "avg(case when type = " + Consts.CARD_TYPE_REV + - " then case when ease > 1 then 1.0 else 0.0 end else null end) as relrnRate, avg(case when type = " + - Consts.CARD_TYPE_REV + - " then time else null end) as relrnTime " + - "from revlog where id > " + - "?", + """select + avg(case when type = $CARD_TYPE_NEW then case when ease > 1 then 1.0 else 0.0 end else null end) as newRate, + avg(case when type = $CARD_TYPE_NEW then time else null end) as newTime, + avg(case when type in ($CARD_TYPE_LRN, $CARD_TYPE_RELEARNING) then case when ease > 1 then 1.0 else 0.0 end else null end) as revRate, + avg(case when type in ($CARD_TYPE_LRN, $CARD_TYPE_RELEARNING) then time else null end) as revTime, + avg(case when type = $CARD_TYPE_REV then case when ease > 1 then 1.0 else 0.0 end else null end) as relrnRate, + avg(case when type = $CARD_TYPE_REV then time else null end) as relrnTime + from revlog where id > ?""", (col.sched.dayCutoff - (10 * SECONDS_PER_DAY)) * 1000, ).use { cur -> if (!cur.moveToFirst()) { diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/CustomStudyDialogTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/CustomStudyDialogTest.kt index f732fa7b4e7c..6c1c41fe7902 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/CustomStudyDialogTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/dialogs/CustomStudyDialogTest.kt @@ -37,6 +37,7 @@ import com.ichi2.utils.KotlinCleanup import org.hamcrest.CoreMatchers.notNullValue import org.hamcrest.MatcherAssert import org.hamcrest.core.IsNull +import org.intellij.lang.annotations.Language import org.json.JSONObject import org.junit.After import org.junit.Test @@ -84,24 +85,26 @@ class CustomStudyDialogTest : RobolectricTest() { customStudy.remove("id") customStudy.remove("mod") customStudy.remove("name") + @Language("JSON") val expected = - "{" + - "\"browserCollapsed\":false," + - "\"collapsed\":false," + - "\"delays\":null," + - "\"desc\":\"\"," + - "\"dyn\":1," + - "\"lrnToday\":[0,0]," + - "\"newToday\":[0,0]," + - "\"previewDelay\":0," + - "\"previewAgainSecs\":60,\"previewHardSecs\":600,\"previewGoodSecs\":0," + - "\"resched\":true," + - "\"revToday\":[0,0]," + - "\"separate\":true," + - "\"terms\":[[\"deck:\\\"Default\\\" prop:due<=1\",99999,6]]," + - "\"timeToday\":[0,0]," + - "\"usn\":-1" + - "}" + """{ + "browserCollapsed":false, + "collapsed":false, + "delays":null, + "desc":"", + "dyn":1, + "lrnToday":[0,0], + "newToday":[0,0], + "previewDelay":0, + "previewAgainSecs":60,"previewHardSecs":600,"previewGoodSecs":0, + "resched":true, + "revToday":[0,0], + "separate":true, + "terms":[["deck:\"Default\" prop:due<=1",99999,6]], + "timeToday":[0,0], + "usn":-1 + } + """ MatcherAssert.assertThat(customStudy, isJsonEqual(JSONObject(expected))) } }