Fix database corruption dialog
This avoids a SQLiteDatabaseCorruptException escaping and ensures the correct dialog is displayed rather than a generic error dialog
Purpose / Description
Accessing the collection with methods like CollectionManger::withCol or CollectionManager::getColUnsafe will attempt to open the database. When the file is corrupt this throws a SQLiteDatabaseCorruptException. In DeckPicker, withCol throws the error which is caught by a launchCatchingTask and a generic error dialog is displayed rather than the intended one.
Fixes
- Fixes #19092
Approach
Rather than accessing the collection object directly, to avoid opening the database the collection file is accessed from CollectionManager, and BackupManager::repairCollection was changed slightly to accommodate this.
Note that the logic in repairCollection does not guarantee the dialog will always appear even if the file is still corrupt (in my testing at least). For example if return repairedFile.renameTo(colFile) succeeds and returns true, no dialog message will appear but the database could still be corrupt and prevent the decks from loading.
I am not sure what the course of action is here, maybe a stronger condition for repairCollection?
How Has This Been Tested?
I am not sure if there is a better way to do this, there probably is. I couldn't find an integration test that corrupts the database, just unit tests (if there isn't maybe this is something worth having).
I copied
Anki-Android-Backend/rsdroid-instrumented/src/androidTest/assets/initial_version_2_12_1_corrupt_regular.anki2
into
AnkiDroid/src/main/assets/.
This file is a corrupt collection used for a backend unit test. Then I modified DeckPicker::handleStartup temporarily for testing:
private fun handleStartup() {
val context = AnkiDroidApp.instance
val environment: AnkiDroidEnvironment =
object : AnkiDroidEnvironment {
private val folder = selectAnkiDroidFolder(context)
override fun hasRequiredPermissions(): Boolean = folder.hasRequiredPermissions(context)
override val requiredPermissions: PermissionSet
get() = folder.permissionSet
override fun initializeAnkiDroidFolder(): Boolean = CollectionHelper.isCurrentAnkiDroidDirAccessible(context)
}
+ val dir = getCurrentAnkiDroidDirectory(context)
+ val targetDb = File(dir, "collection.anki2")
+ this.assets.open("initial_version_2_12_1_corrupt_regular.anki2").use { input ->
+ targetDb.outputStream().use { output ->
+ input.copyTo(output)
+ }
+ }
viewModel.handleStartup(environment = environment)
}
and temporarily modified BackupManager::repairCollection to always return false to guarantee the dialog would appear during testing.
- Automated test suite
- Manually on Emulator
- Pixel 6 Pro API 31
Screencast from 12-05-2025 05:57:47 AM.webm
Learning (optional, can help others)
Describe the research stage
Links to blog posts, patterns, libraries or addons used to solve this problem
Checklist
Please, go through these checks before submitting the PR.
- [x] You have a descriptive commit message with a short title (first line, max 50 chars).
- [x] You have commented your code, particularly in hard-to-understand areas
- [x] You have performed a self-review of your own code
- [x] UI changes: include screenshots of all affected screens (in particular showing any new or changed strings)
- [ ] UI Changes: You have tested your change using the Google Accessibility Scanner