diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt index db405ca527f6..4efb8e0b9bc8 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt @@ -20,6 +20,8 @@ import android.app.Activity import android.app.Dialog import android.content.Context import android.content.DialogInterface +import android.content.Intent +import android.provider.Settings import android.view.WindowManager import android.view.WindowManager.BadTokenException import androidx.annotation.StringRes @@ -60,6 +62,9 @@ import net.ankiweb.rsdroid.exceptions.BackendNetworkException import net.ankiweb.rsdroid.exceptions.BackendSyncException import org.jetbrains.annotations.VisibleForTesting import timber.log.Timber +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.util.Locale import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlin.coroutines.resume @@ -169,8 +174,12 @@ suspend fun FragmentActivity.runCatching( Timber.w(exc, errorMessage) exc.localizedMessage?.let { showSnackbar(it) } } - is BackendNetworkException, is BackendSyncException -> { - // these exceptions do not generate worthwhile crash reports + is BackendSyncException -> { + // this exceptions do not generate worthwhile crash reports + showInvalidClockError(this, exc.localizedMessage!!, exc, false) + } + is BackendNetworkException -> { + // this exceptions do not generate worthwhile crash reports showError(this, exc.localizedMessage!!, exc, false) } is BackendException -> { @@ -278,6 +287,38 @@ fun showError(context: Context, msg: String, exception: Throwable, crashReport: } } +fun showInvalidClockError(context: Context, msg: String, exception: Throwable, crashReport: Boolean = true) { + if (throwOnShowError) throw IllegalStateException("throwOnShowError: $msg", exception) + try { + val currentTime = LocalDateTime.now() + val dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss a", Locale.getDefault()) + val formattedTime = currentTime.format(dateFormatter) + + val fullMessage = "$msg\n\nYour clock is currently set to:\n$formattedTime" + AlertDialog.Builder(context).show { + title(R.string.vague_error) + message(text = fullMessage) + positiveButton(R.string.dialog_go_to_setting) { + // Navigate to Date and Time settings + val intent = Intent(Settings.ACTION_DATE_SETTINGS) + context.startActivity(intent) + } + negativeButton(R.string.dialog_cancel) + if (crashReport) { + setOnDismissListener { + CrashReportService.sendExceptionReport( + exception, + origin = context::class.java.simpleName + ) + } + } + } + } catch (ex: BadTokenException) { + // issue 12718: activity provided by `context` was not running + Timber.w(ex, "unable to display error dialog") + } +} + /** In most cases, you'll want [AnkiActivity.withProgress] * instead. This lower-level routine can be used to integrate your own * progress UI. diff --git a/AnkiDroid/src/main/res/values/03-dialogs.xml b/AnkiDroid/src/main/res/values/03-dialogs.xml index 920af4963e01..03d87dfe90e1 100644 --- a/AnkiDroid/src/main/res/values/03-dialogs.xml +++ b/AnkiDroid/src/main/res/values/03-dialogs.xml @@ -87,6 +87,7 @@ Overwrite Repair Replace + Go to setting The path specified wasn’t a valid directory The provided text does not resolve to a valid PEM-encoded X509 certificate