diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt index 70c5b5f6c6eb..1915188d1d9e 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt @@ -16,8 +16,11 @@ package com.ichi2.anki +import android.app.Activity import android.content.Context import androidx.appcompat.app.AlertDialog +import androidx.fragment.app.Fragment +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.coroutineScope import anki.collection.Progress import com.ichi2.anki.UIUtils.showSimpleSnackbar @@ -232,3 +235,33 @@ suspend fun AnkiActivity.userAcceptsSchemaChange(col: Collection): Boolean { .show() } } + +/** + * Launch a job that catches any uncaught errors, informs the user and prints it to Log. + * Errors from the backend contain localized text that is often suitable to show to the user as-is. + * Other errors should ideally be handled in the block. + */ +fun LifecycleOwner.catchingLifecycleScope( + activity: Activity, + errorMessage: String? = null, + block: suspend CoroutineScope.() -> Unit +): Job = lifecycle.coroutineScope.launch { + try { + block() + } catch (e: CancellationException) { + throw e + } catch (e: Exception) { + // TODO: localize + Timber.w(e, errorMessage) + UIUtils.showDismissibleSnackbar(activity, "An error occurred: $e", R.string.close) + CrashReportService.sendExceptionReport(e, activity::class.java.simpleName) + } +} + +/** + * @see [LifecycleOwner.catchingLifecycleScope] + */ +fun Fragment.catchingLifecycleScope( + errorMessage: String? = null, + block: suspend CoroutineScope.() -> Unit +) = (this as LifecycleOwner).catchingLifecycleScope(requireActivity(), errorMessage, block) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Statistics.kt b/AnkiDroid/src/main/java/com/ichi2/anki/Statistics.kt index 81a331c53382..092e3ee56c1b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/Statistics.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/Statistics.kt @@ -44,7 +44,6 @@ import com.ichi2.anki.stats.AnkiStatsTaskHandler import com.ichi2.anki.stats.AnkiStatsTaskHandler.Companion.getInstance import com.ichi2.anki.stats.ChartView import com.ichi2.anki.widgets.DeckDropDownAdapter.SubtitleListener -import com.ichi2.async.catchingLifecycleScope import com.ichi2.libanki.Collection import com.ichi2.libanki.DeckId import com.ichi2.libanki.Decks @@ -451,7 +450,7 @@ class Statistics : NavigationDrawerActivity(), DeckSelectionListener, SubtitleLi private fun createStatisticOverview() { val handler = (requireActivity() as Statistics).taskHandler - statisticsJob = catchingLifecycleScope(requireActivity(), "createStatisticOverview failed with error") { + statisticsJob = catchingLifecycleScope("createStatisticOverview failed with error") { handler.createStatisticsOverview(mWebView, mProgressBar) } } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/UIUtils.kt b/AnkiDroid/src/main/java/com/ichi2/anki/UIUtils.kt index 4773de51d8c1..8bd4d016261f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/UIUtils.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/UIUtils.kt @@ -141,7 +141,7 @@ object UIUtils { } @JvmStatic - fun getDismissibleSnackbar(activity: Activity?, mainText: String, length: Int, dismissTextResource: Int, root: View): Snackbar { + fun getDismissibleSnackbar(context: Context, mainText: String, length: Int, dismissTextResource: Int, root: View): Snackbar { val sb = Snackbar.make(root, mainText, length) sb.setAction(dismissTextResource) { sb.dismiss() @@ -152,7 +152,7 @@ object UIUtils { val action = view.findViewById(com.google.android.material.R.id.snackbar_action) if (tv != null && action != null) { tv.setTextColor(Color.WHITE) - action.setTextColor(ContextCompat.getColor(activity!!, R.color.material_light_blue_500)) + action.setTextColor(ContextCompat.getColor(context, R.color.material_light_blue_500)) tv.maxLines = 2 // prevent tablets from truncating to 1 line } return sb diff --git a/AnkiDroid/src/main/java/com/ichi2/async/CoroutineHelpers.kt b/AnkiDroid/src/main/java/com/ichi2/async/CoroutineHelpers.kt deleted file mode 100644 index c1ade807f1df..000000000000 --- a/AnkiDroid/src/main/java/com/ichi2/async/CoroutineHelpers.kt +++ /dev/null @@ -1,54 +0,0 @@ -/**************************************************************************************** - * Copyright (c) 2022 Divyansh Kushwaha * - * * - * This program is free software; you can redistribute it and/or modify it under * - * the terms of the GNU General Public License as published by the Free Software * - * Foundation; either version 3 of the License, or (at your option) any later * - * version. * - * * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY * - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * - * PARTICULAR PURPOSE. See the GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License along with * - * this program. If not, see . * - ****************************************************************************************/ - -/* -* This file contains extension functions for different coroutine related actions. -*/ -package com.ichi2.async - -import android.app.Activity -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.coroutineScope -import com.ichi2.anki.CrashReportService -import com.ichi2.anki.R -import com.ichi2.anki.UIUtils.showDismissibleSnackbar -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.launch -import timber.log.Timber - -/* - * Launch a job that catches any uncaught errors, informs the user and prints it to Log. - * Errors from the backend contain localized text that is often suitable to show to the user as-is. - * Other errors should ideally be handled in the block. - */ -fun LifecycleOwner.catchingLifecycleScope( - activity: Activity, - errorMessage: String? = null, - block: suspend CoroutineScope.() -> Unit -): Job = lifecycle.coroutineScope.launch { - try { - block() - } catch (e: CancellationException) { - throw e - } catch (e: Exception) { - // TODO: localize - Timber.w(e, errorMessage) - showDismissibleSnackbar(activity, "An error occurred: $e", R.string.close) - CrashReportService.sendExceptionReport(e, activity::class.java.simpleName) - } -}