Skip to content

Commit c0eaf4f

Browse files
david-allisonmikehardy
authored andcommitted
refactor: AnkiDroidApp initialization error types
This code was converted from Java, and needs to be extended to handle other types of error for issue 13207 For this, a `sealed class` which contains each type of initialization error is a reusable abstraction Related to issue 13207
1 parent 167e9b1 commit c0eaf4f

File tree

3 files changed

+40
-26
lines changed

3 files changed

+40
-26
lines changed

AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ import java.util.Locale
7979
open class AnkiDroidApp :
8080
Application(),
8181
ChangeManager.Subscriber {
82-
/** An exception if the WebView subsystem fails to load */
83-
private var webViewError: Throwable? = null
82+
/** An exception if AnkiDroidApp fails to load */
83+
private var fatalInitializationError: FatalInitializationError? = null
8484

8585
@LegacyNotifications("The widget triggers notifications by posting null to this, but we plan to stop relying on the widget")
8686
private val notifications = MutableLiveData<Void?>()
@@ -302,7 +302,7 @@ open class AnkiDroidApp :
302302
// 5794: Errors occur if the WebView fails to load
303303
// android.webkit.WebViewFactory.MissingWebViewPackageException.MissingWebViewPackageException
304304
// Error may be excessive, but I expect a UnsatisfiedLinkError to be possible here.
305-
webViewError = e
305+
fatalInitializationError = FatalInitializationError.WebViewError(e)
306306
sendExceptionReport(e, "setAcceptFileSchemeCookies")
307307
Timber.e(e, "setAcceptFileSchemeCookies")
308308
false
@@ -474,16 +474,24 @@ open class AnkiDroidApp :
474474
else -> appResources.getString(R.string.link_manual)
475475
}
476476

477-
fun webViewFailedToLoad(): Boolean = instance.webViewError != null
477+
/** (optional) set if an unrecoverable error occurs during Application startup */
478+
val fatalError: FatalInitializationError?
479+
get() = instance.fatalInitializationError
480+
}
481+
}
478482

479-
val webViewErrorMessage: String?
480-
get() {
481-
val error = instance.webViewError
482-
if (error == null) {
483-
Timber.w("getWebViewExceptionMessage called without webViewFailedToLoad check")
484-
return null
485-
}
486-
return ExceptionUtil.getExceptionMessage(error)
483+
/**
484+
* Types of unrecoverable errors which we want to inform the user of
485+
*/
486+
sealed class FatalInitializationError {
487+
data class WebViewError(
488+
val error: Throwable,
489+
) : FatalInitializationError()
490+
491+
/** Advanced/developer-facing string representing the error */
492+
val errorDetail: String
493+
get() =
494+
when (this) {
495+
is WebViewError -> ExceptionUtil.getExceptionMessage(error)
487496
}
488-
}
489497
}

AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ import com.ichi2.anki.InitialActivity.StartupFailure.DirectoryNotAccessible
9090
import com.ichi2.anki.InitialActivity.StartupFailure.DiskFull
9191
import com.ichi2.anki.InitialActivity.StartupFailure.FutureAnkidroidVersion
9292
import com.ichi2.anki.InitialActivity.StartupFailure.SDCardNotMounted
93-
import com.ichi2.anki.InitialActivity.StartupFailure.WebviewFailed
9493
import com.ichi2.anki.IntentHandler.Companion.intentToReviewDeckFromShortcuts
9594
import com.ichi2.anki.account.AccountActivity
9695
import com.ichi2.anki.analytics.UsageAnalytics
@@ -999,16 +998,10 @@ open class DeckPicker :
999998
Timber.i("Displaying database locked error")
1000999
showDatabaseErrorDialog(DatabaseErrorDialogType.DIALOG_DB_LOCKED)
10011000
}
1002-
is WebviewFailed ->
1001+
is StartupFailure.InitializationError ->
10031002
AlertDialog.Builder(this).show {
10041003
title(R.string.ankidroid_init_failed_webview_title)
1005-
message(
1006-
text =
1007-
getString(
1008-
R.string.ankidroid_init_failed_webview,
1009-
AnkiDroidApp.webViewErrorMessage,
1010-
),
1011-
)
1004+
message(text = failure.toHumanReadableString(this@DeckPicker))
10121005
positiveButton(R.string.close) {
10131006
closeCollectionAndFinish()
10141007
}

AnkiDroid/src/main/java/com/ichi2/anki/InitialActivity.kt

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,8 @@ object InitialActivity {
4848
/** Returns null on success */
4949
@CheckResult
5050
fun getStartupFailureType(initializeAnkiDroidDirectory: () -> Boolean): StartupFailure? {
51-
// A WebView failure means that we skip `AnkiDroidApp`, and therefore haven't loaded the collection
52-
if (AnkiDroidApp.webViewFailedToLoad()) {
53-
return StartupFailure.WebviewFailed
51+
AnkiDroidApp.fatalError?.let {
52+
return StartupFailure.InitializationError(it)
5453
}
5554

5655
val failure =
@@ -150,7 +149,21 @@ object InitialActivity {
150149

151150
data object DatabaseLocked : StartupFailure()
152151

153-
data object WebviewFailed : StartupFailure()
152+
/**
153+
* [AnkiDroidApp] encountered a fatal error
154+
*/
155+
data class InitializationError(
156+
val error: FatalInitializationError,
157+
) : StartupFailure() {
158+
fun toHumanReadableString(context: Context): String =
159+
when (error) {
160+
is FatalInitializationError.WebViewError ->
161+
context.getString(
162+
R.string.ankidroid_init_failed_webview,
163+
error.errorDetail,
164+
)
165+
}
166+
}
154167

155168
data object DiskFull : StartupFailure()
156169
}

0 commit comments

Comments
 (0)