Skip to content

Commit

Permalink
Require V1 scheduler users to update when they try to study
Browse files Browse the repository at this point in the history
Studying with the V1 scheduler enabled is no longer possible on recent
Anki, AnkiMobile or AnkiWeb versions, so AnkiDroid is the last one
still supporting it. Pushing users to upgrade will save them from some
of the footguns V1 had, and will allow AnkiDroid to cut out some code
and tests. Many users have likely already upgraded due to the use of
the other clients.

This commit adds support for the backend upgrade code, so that learning
cards will not be reset on upgrade. To make use of this, users will be
automatically updated to the latest schema version, the scheduler
upgrade will be performed, and then they're moved back to the legacy
schema.

I've also added a helper to more ergonomically deal with schema changes.
  • Loading branch information
dae committed Jul 24, 2022
1 parent b18a23d commit 1fb870b
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 2 deletions.
29 changes: 28 additions & 1 deletion AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,16 @@ import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.coroutineScope
import anki.collection.Progress
import com.ichi2.anki.UIUtils.showSimpleSnackbar
import com.ichi2.libanki.Collection
import com.ichi2.libanki.CollectionV16
import com.ichi2.themes.StyledProgressDialog
import kotlinx.coroutines.*
import net.ankiweb.rsdroid.Backend
import net.ankiweb.rsdroid.BackendException
import net.ankiweb.rsdroid.exceptions.BackendInterruptedException
import timber.log.Timber
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine

/**
* Launch a job that catches any uncaught errors and reports them to the user.
Expand Down Expand Up @@ -127,7 +130,7 @@ suspend fun <T> AnkiActivity.runInBackgroundWithProgress(
* window with the provided message.
*/
suspend fun <T> AnkiActivity.runInBackgroundWithProgress(
message: String = "",
message: String = resources.getString(R.string.dialog_processing),
op: suspend () -> T
): T = withProgressDialog(
context = this@runInBackgroundWithProgress,
Expand Down Expand Up @@ -205,3 +208,27 @@ private fun ProgressContext.updateDialog(dialog: android.app.ProgressDialog) {
@Suppress("Deprecation") // ProgressDialog deprecation
dialog.setMessage(text + progressText)
}

/**
* If a full sync is not already required, confirm the user wishes to proceed.
* If the user agrees, the schema is bumped and the routine will return true.
* On false, calling routine should abort.
*/
suspend fun AnkiActivity.userAcceptsSchemaChange(col: Collection): Boolean {
if (col.schemaChanged()) {
return true
}
return suspendCoroutine { coroutine ->
AlertDialog.Builder(this)
// generic message
.setMessage(col.tr.deckConfigWillRequireFullSync())
.setPositiveButton(R.string.dialog_ok) { _, _ ->
col.modSchemaNoCheck()
coroutine.resume(true)
}
.setNegativeButton(R.string.dialog_cancel) { _, _ ->
coroutine.resume(false)
}
.show()
}
}
61 changes: 61 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ import android.view.WindowManager.BadTokenException
import android.widget.*
import androidx.annotation.StringRes
import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SearchView
import androidx.core.app.ActivityCompat
import androidx.core.app.ActivityCompat.OnRequestPermissionsResultCallback
Expand Down Expand Up @@ -95,6 +96,7 @@ import com.ichi2.libanki.Collection.CheckDatabaseResult
import com.ichi2.libanki.importer.AnkiPackageImporter
import com.ichi2.libanki.sched.AbstractDeckTreeNode
import com.ichi2.libanki.sched.TreeNode
import com.ichi2.libanki.sched.upgradeScheduler
import com.ichi2.libanki.sync.CustomSyncServerUrlException
import com.ichi2.libanki.sync.Syncer.ConnectionResultType
import com.ichi2.libanki.utils.TimeManager
Expand Down Expand Up @@ -1950,11 +1952,41 @@ open class DeckPicker :
}
}

private fun promptUserToUpdateScheduler() {
val builder = AlertDialog.Builder(this)
.setMessage(col.tr.schedulingUpdateRequired())
.setPositiveButton(R.string.dialog_ok) { _, _ ->
launchCatchingTask {
if (!userAcceptsSchemaChange(col)) {
return@launchCatchingTask
}
runInBackgroundWithProgress {
CollectionHelper.getInstance().updateScheduler(this@DeckPicker)
}
showThemedToast(this@DeckPicker, col.tr.schedulingUpdateDone(), false)
refreshState()
}
}
.setNegativeButton(R.string.dialog_cancel) { _, _ ->
// nothing to do
}
if (AdaptionUtil.hasWebBrowser(this)) {
builder.setNeutralButton(col.tr.schedulingUpdateMoreInfoButton()) { _, _ ->
this.openUrl(Uri.parse("https://faqs.ankiweb.net/the-anki-2.1-scheduler.html#updating"))
}
}
builder.show()
}

private fun handleDeckSelection(did: Long, selectionType: DeckSelectionType) {
// Clear the undo history when selecting a new deck
if (col.decks.selected() != did) {
col.clearUndo()
}
if (col.get_config_int("schedVer") == 1) {
promptUserToUpdateScheduler()
return
}
// Select the deck
col.decks.select(did)
// Also forget the last deck used by the Browser
Expand Down Expand Up @@ -2655,3 +2687,32 @@ open class DeckPicker :
}
}
}

/** Upgrade from v1 to v2 scheduler.
* Caller must have confirmed schema modification already.
*/
@KotlinCleanup("move into CollectionHelper once it's converted to Kotlin")
@Synchronized
fun CollectionHelper.updateScheduler(context: Context) {
if (BackendFactory.defaultLegacySchema) {
// We'll need to temporarily update to the latest schema.
closeCollection(true, "sched upgrade")
discardBackend()
BackendFactory.defaultLegacySchema = false
// Ensure collection closed if upgrade fails, and schema reverted
// even if close fails.
try {
try {
getCol(context).newBackend.upgradeScheduler()
} finally {
closeCollection(true, "sched upgrade")
}
} finally {
BackendFactory.defaultLegacySchema = true
discardBackend()
}
} else {
// Can upgrade directly
getCol(context).newBackend.upgradeScheduler()
}
}
2 changes: 1 addition & 1 deletion AnkiDroid/src/main/java/com/ichi2/libanki/Collection.kt
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ open class Collection(
}

// Note: Additional members in the class duplicate this
private fun _loadScheduler() {
fun _loadScheduler() {
val ver = schedVer()
if (ver == 1) {
sched = Sched(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,10 @@ fun CollectionV16.deckTreeLegacy(includeCounts: Boolean): List<TreeNode<DeckDueT
}
return toLegacyNode(deckTree(includeCounts), "").children
}

fun CollectionV16.upgradeScheduler() {
modSchema()
clearUndo()
backend.upgradeScheduler()
_loadScheduler()
}
1 change: 1 addition & 0 deletions AnkiDroid/src/main/res/values/03-dialogs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@
<string name="dialog_ok">OK</string>
<string name="dialog_no">No</string>
<string name="dialog_continue">Continue</string>
<string name="dialog_processing">Processing...</string>
<string name="dialog_positive_create" comment="Create a new collection, probably erasing the previous one, may be necessary in case of error.">Create</string>
<string name="dialog_positive_delete">Delete</string>
<!-- Currently unsued, should be useful in the future -->
Expand Down

0 comments on commit 1fb870b

Please sign in to comment.