From b9712a845267df7d473d7caee5bcca33ac9acc73 Mon Sep 17 00:00:00 2001 From: Ashish Yadav <48384865+criticalAY@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:47:44 +0530 Subject: [PATCH 1/3] refactor: add initializeAcraCrashReporter() top-level function Extract CrashReportService.initialize() and isProperServiceProcess() into top-level functions. --- AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt | 4 ++-- .../src/main/java/com/ichi2/anki/CrashReportService.kt | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt index d91e85ca3f08..1f29df434c84 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt @@ -136,7 +136,7 @@ open class AnkiDroidApp : // Ensures any change is propagated to widgets ChangeManager.subscribe(this) - CrashReportService.initialize(this) + initializeAcraCrashReporter() val logType = LogType.value when (logType) { LogType.DEBUG -> Timber.plant(DebugTree()) @@ -167,7 +167,7 @@ open class AnkiDroidApp : } // Stop after analytics and logging are initialised. - if (CrashReportService.isProperServiceProcess()) { + if (isAcraSenderProcess()) { Timber.d("Skipping AnkiDroidApp.onCreate from ACRA sender process") return } diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CrashReportService.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CrashReportService.kt index d6e9cb66f248..05c1662776d1 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CrashReportService.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CrashReportService.kt @@ -417,3 +417,13 @@ fun runCatchingWithReport( if (e is Error) throw e Result.failure(e) } + +/** + * Initializes ACRA crash reporting. + */ +context(application: Application) +fun initializeAcraCrashReporter() { + CrashReportService.initialize(application) +} + +fun isAcraSenderProcess(): Boolean = CrashReportService.isProperServiceProcess() From 679c7f027c25831c560ade02ccd50a242d6cabc3 Mon Sep 17 00:00:00 2001 From: Ashish Yadav <48384865+criticalAY@users.noreply.github.com> Date: Fri, 17 Apr 2026 23:58:30 +0530 Subject: [PATCH 2/3] refctor: extract crash reporting interface to :common:android As we move towards a multi-module architecture, feature modules need crash reporting without depending on the app module. Without this, each feature module would need its own bridge interface (e.g. WidgetCrashReporter, BrowserCrashReporter) duplicating the same pattern everywhere. This does use android dependencies though and those don't seem removable, so it gets the android-capable sub-module of common --- .../java/com/ichi2/anki/tests/ACRATest.kt | 34 ++-- ...hReportService.kt => AcraCrashReporter.kt} | 106 ++++------ .../analytics/AnkiDroidCrashReportDialog.kt | 9 +- .../preferences/DeveloperOptionsFragment.kt | 2 +- .../anki/servicelayer/DebugInfoService.kt | 2 +- .../java/com/ichi2/anki/CrashReportService.kt | 192 ++++++++++++++++++ 6 files changed, 263 insertions(+), 82 deletions(-) rename AnkiDroid/src/main/java/com/ichi2/anki/{CrashReportService.kt => AcraCrashReporter.kt} (87%) create mode 100644 common/android/src/main/java/com/ichi2/anki/CrashReportService.kt diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt index cd5b17b5901c..3cb0d6a51ded 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt @@ -23,13 +23,17 @@ import androidx.test.annotation.UiThreadTest import androidx.test.ext.junit.runners.AndroidJUnit4 import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.CrashReportService -import com.ichi2.anki.CrashReportService.FEEDBACK_REPORT_ALWAYS -import com.ichi2.anki.CrashReportService.FEEDBACK_REPORT_ASK +import com.ichi2.anki.CrashReporter +import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_ALWAYS +import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_ASK import com.ichi2.anki.R +import com.ichi2.anki.acraCoreConfigBuilder import com.ichi2.anki.analytics.UsageAnalytics import com.ichi2.anki.logging.ProductionCrashReportingTree import com.ichi2.anki.preferences.sharedPrefs import com.ichi2.anki.servicelayer.ThrowableFilterService +import com.ichi2.anki.setDebugACRAConfig +import com.ichi2.anki.setProductionACRAConfig import com.ichi2.anki.testutil.GrantStoragePermission import org.acra.ACRA import org.acra.builder.ReportBuilder @@ -70,7 +74,7 @@ class ACRATest : InstrumentedTest() { @Throws(Exception::class) fun testDebugConfiguration() { // Debug mode overrides all saved state so no setup needed - CrashReportService.setDebugACRAConfig(sharedPrefs) + setDebugACRAConfig(sharedPrefs) assertArrayEquals( "Debug logcat arguments not set correctly", CrashReportService.acraCoreConfigBuilder @@ -90,9 +94,9 @@ class ACRATest : InstrumentedTest() { ) assertEquals( "ACRA feedback was not turned off correctly", - CrashReportService.FEEDBACK_REPORT_NEVER, + CrashReporter.FEEDBACK_REPORT_NEVER, sharedPrefs - .getString(CrashReportService.FEEDBACK_REPORT_KEY, "undefined"), + .getString(CrashReporter.FEEDBACK_REPORT_KEY, "undefined"), ) } @@ -100,11 +104,11 @@ class ACRATest : InstrumentedTest() { @Throws(Exception::class) fun testProductionConfigurationUserDisabled() { // set up as if the user had prefs saved to disable completely - setReportConfig(CrashReportService.FEEDBACK_REPORT_NEVER) + setReportConfig(CrashReporter.FEEDBACK_REPORT_NEVER) // ACRA initializes production logcat via annotation and we can't mock Build.DEBUG // That means we are restricted from verifying production logcat args and this is the debug case again - CrashReportService.setProductionACRAConfig(sharedPrefs) + setProductionACRAConfig(sharedPrefs) verifyDebugACRAPreferences() } @@ -115,7 +119,7 @@ class ACRATest : InstrumentedTest() { setReportConfig(FEEDBACK_REPORT_ASK) // If the user is set to ask, then it's production, with interaction mode dialog - CrashReportService.setProductionACRAConfig(sharedPrefs) + setProductionACRAConfig(sharedPrefs) verifyACRANotDisabled() assertToastMessage(R.string.feedback_for_manual_toast_text) @@ -134,7 +138,7 @@ class ACRATest : InstrumentedTest() { // If the user is set to always, then it's production, with interaction mode toast // will be useful with ACRA 5.2.0 - CrashReportService.setProductionACRAConfig(sharedPrefs) + setProductionACRAConfig(sharedPrefs) // The same class/method combo is only sent once, so we face a new method each time (should test that system later) val crash = Exception("testCrashReportSend at " + System.currentTimeMillis()) @@ -173,7 +177,7 @@ class ACRATest : InstrumentedTest() { ) // Now let's clear data - CrashReportService.deleteACRALimiterData(testContext) + CrashReportService.deleteLimiterData(testContext) // A third send should work again assertTrue( @@ -192,7 +196,7 @@ class ACRATest : InstrumentedTest() { setReportConfig(FEEDBACK_REPORT_ALWAYS) // If the user is set to always, then it's production, with interaction mode toast - CrashReportService.setProductionACRAConfig(sharedPrefs) + setProductionACRAConfig(sharedPrefs) verifyACRANotDisabled() assertToastMessage(R.string.feedback_auto_toast_text) @@ -207,7 +211,7 @@ class ACRATest : InstrumentedTest() { setReportConfig(FEEDBACK_REPORT_ALWAYS) // If the user is set to ask, then it's production, with interaction mode dialog - CrashReportService.setProductionACRAConfig(sharedPrefs) + setProductionACRAConfig(sharedPrefs) verifyACRANotDisabled() assertDialogEnabledStatus("dialog should be disabled when status is ALWAYS", false) @@ -226,7 +230,7 @@ class ACRATest : InstrumentedTest() { setReportConfig(FEEDBACK_REPORT_ASK) // If the user is set to ask, then it's production, with interaction mode dialog - CrashReportService.setProductionACRAConfig(sharedPrefs) + setProductionACRAConfig(sharedPrefs) verifyACRANotDisabled() assertToastMessage(R.string.feedback_for_manual_toast_text) @@ -282,7 +286,7 @@ class ACRATest : InstrumentedTest() { } private fun setAcraReportingMode(feedbackReportAlways: String) { - CrashReportService.setAcraReportingMode(feedbackReportAlways) + CrashReportService.setReportingMode(feedbackReportAlways) } @Throws(ACRAConfigurationException::class) @@ -333,7 +337,7 @@ class ACRATest : InstrumentedTest() { } private fun setReportConfig(feedbackReportAsk: String) { - sharedPrefs.edit { putString(CrashReportService.FEEDBACK_REPORT_KEY, feedbackReportAsk) } + sharedPrefs.edit { putString(CrashReporter.FEEDBACK_REPORT_KEY, feedbackReportAsk) } } private val sharedPrefs: SharedPreferences diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CrashReportService.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AcraCrashReporter.kt similarity index 87% rename from AnkiDroid/src/main/java/com/ichi2/anki/CrashReportService.kt rename to AnkiDroid/src/main/java/com/ichi2/anki/AcraCrashReporter.kt index 05c1662776d1..982a8c2843e4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CrashReportService.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AcraCrashReporter.kt @@ -23,6 +23,10 @@ import androidx.annotation.VisibleForTesting import androidx.core.content.edit import androidx.core.content.pm.PackageInfoCompat import androidx.webkit.WebViewCompat +import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_ALWAYS +import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_ASK +import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_KEY +import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_NEVER import com.ichi2.anki.analytics.AnkiDroidCrashReportDialog import com.ichi2.anki.analytics.UsageAnalytics import com.ichi2.anki.analytics.UsageAnalytics.sendAnalyticsException @@ -43,13 +47,7 @@ import org.acra.config.ToastConfigurationBuilder import org.acra.sender.HttpSender import timber.log.Timber -object CrashReportService { - // ACRA constants used for stored preferences - const val FEEDBACK_REPORT_KEY = "reportErrorMode" - const val FEEDBACK_REPORT_ASK = "2" - const val FEEDBACK_REPORT_NEVER = "1" - const val FEEDBACK_REPORT_ALWAYS = "0" - +private object AcraCrashReporter : CrashReporter { /** Our ACRA configurations, initialized during Application.onCreate() */ @JvmStatic private var logcatArgs = @@ -166,7 +164,7 @@ object CrashReportService { */ @JvmStatic fun initialize(application: Application) { - CrashReportService.application = application + this.application = application // FIXME ACRA needs to reinitialize after language is changed, but with the new language // this is difficult because the Application (AnkiDroidApp) does not change it's baseContext // perhaps a solution could be to change AnkiDroidApp to have a context wrapper that it sets @@ -174,7 +172,7 @@ object CrashReportService { // in GeneralSettingsFragment for the language dialog change listener, the context wrapper // could be updated directly with the new locale code so that calling getString on would fetch // the new language string ? - toastText = ToastType.AUTO_TOAST.getToastMessage(CrashReportService.application) + toastText = ToastType.AUTO_TOAST.getToastMessage(application) // Setup logging and crash reporting if (BuildConfig.DEBUG) { @@ -195,7 +193,7 @@ object CrashReportService { * Set the reporting mode for ACRA based on the value of the FEEDBACK_REPORT_KEY preference * @param value value of FEEDBACK_REPORT_KEY preference */ - fun setAcraReportingMode(value: String) { + override fun setReportingMode(value: String) { application.sharedPrefs().edit { // Set the ACRA disable value if (value == FEEDBACK_REPORT_NEVER) { @@ -225,7 +223,7 @@ object CrashReportService { @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) fun setDebugACRAConfig(prefs: SharedPreferences) { // Disable crash reporting - setAcraReportingMode(FEEDBACK_REPORT_NEVER) + setReportingMode(FEEDBACK_REPORT_NEVER) prefs.edit { putString(FEEDBACK_REPORT_KEY, FEEDBACK_REPORT_NEVER) } // Use a wider logcat filter in case crash reporting manually re-enabled logcatArgs = arrayOf("-t", "1500", "-v", "long", "ACRA:S") @@ -240,7 +238,7 @@ object CrashReportService { @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) fun setProductionACRAConfig(prefs: SharedPreferences) { // Enable or disable crash reporting based on user setting - setAcraReportingMode(prefs.getString(FEEDBACK_REPORT_KEY, FEEDBACK_REPORT_ASK)!!) + setReportingMode(prefs.getString(FEEDBACK_REPORT_KEY, FEEDBACK_REPORT_ASK)!!) } private fun fetchWebViewInformation(): HashMap { @@ -262,17 +260,24 @@ object CrashReportService { } /** Used when we don't have an exception to throw, but we know something is wrong and want to diagnose it */ - fun sendExceptionReport( + override fun sendExceptionReport( message: String?, origin: String?, ) = sendExceptionReport(ManuallyReportedException(message), origin) - fun sendExceptionReport( + override fun sendExceptionReport( e: Throwable, origin: String?, - additionalInfo: String? = null, - onlyIfSilent: Boolean = false, - context: Context = application.applicationContext, + additionalInfo: String?, + onlyIfSilent: Boolean, + ) = sendExceptionReport(e, origin, additionalInfo, onlyIfSilent, application.applicationContext) + + override fun sendExceptionReport( + e: Throwable, + origin: String?, + additionalInfo: String?, + onlyIfSilent: Boolean, + context: Context, ) { sendAnalyticsException(e, false) AnkiDroidApp.sentExceptionReportHack = true @@ -297,7 +302,7 @@ object CrashReportService { fun isProperServiceProcess(): Boolean = ACRA.isACRASenderServiceProcess() - fun isAcraEnabled( + override fun isEnabled( context: Context, defaultValue: Boolean, ): Boolean { @@ -314,7 +319,7 @@ object CrashReportService { * * @param context the context leading to the directory with ACRA limiter data */ - fun deleteACRALimiterData(context: Context) { + override fun deleteLimiterData(context: Context) { try { LimiterData().store(context) } catch (e: Exception) { @@ -322,13 +327,13 @@ object CrashReportService { } } - fun onPreferenceChanged( + override fun onPreferenceChanged( ctx: Context, newValue: String, ) { - setAcraReportingMode(newValue) + setReportingMode(newValue) // If the user changed error reporting, make sure future reports have a chance to post - deleteACRALimiterData(ctx) + deleteLimiterData(ctx) // We also need to re-chain our UncaughtExceptionHandlers UsageAnalytics.reInitialize() ThrowableFilterService.reInitialize() @@ -338,7 +343,8 @@ object CrashReportService { * @return the status of the report, true if the report was sent, false if the report is already * submitted */ - fun sendReport(ankiActivity: AnkiActivity): Boolean { + override fun sendReport(activity: android.app.Activity): Boolean { + val ankiActivity = activity as AnkiActivity val preferences = ankiActivity.sharedPrefs() val reportMode = preferences.getString(FEEDBACK_REPORT_KEY, "") return if (FEEDBACK_REPORT_NEVER == reportMode) { @@ -359,7 +365,7 @@ object CrashReportService { val currentTimestamp = TimeManager.time.intTimeMS() val lastReportTimestamp = getTimestampOfLastReport(activity) return if (currentTimestamp - lastReportTimestamp > MIN_INTERVAL_MS) { - deleteACRALimiterData(activity) + deleteLimiterData(activity) sendExceptionReport( UserSubmittedException(EXCEPTION_MESSAGE), "AnkiDroidApp.HelpDialog", @@ -385,45 +391,23 @@ object CrashReportService { } /** - * Runs the provided block, catching [Exception], logging it and reporting it to [CrashReportService] - * - * **Example** - * ``` - * runCatchingWithReport("callingMethod", onlyIfSilent = true) { - * doSomethingRisky() - * } - * ``` - * - * **Note**: This differs from [runCatching] - `Error` is thrown - * - * @param origin Data logged to Timber, and provided as the 'origin' field in the error report - * @param onlyIfSilent Skip crash report if the crash reporting service is not 'always accept' - * @param block Code to execute - * - * @throws Error If raised, this will be reported and rethrown - * - * @return A Result containing either the successful result of [block] or the [Exception] thrown - */ -fun runCatchingWithReport( - origin: String?, - onlyIfSilent: Boolean = false, - block: () -> T, -): Result = - try { - Result.success(block()) - } catch (e: Throwable) { - Timber.w(e, origin) - CrashReportService.sendExceptionReport(e, origin, onlyIfSilent = onlyIfSilent) - if (e is Error) throw e - Result.failure(e) - } - -/** - * Initializes ACRA crash reporting. + * Initializes ACRA crash reporting and wires it up as the + * global [CrashReportService] reporter. */ context(application: Application) fun initializeAcraCrashReporter() { - CrashReportService.initialize(application) + AcraCrashReporter.initialize(application) + CrashReportService.setReporter(AcraCrashReporter) } -fun isAcraSenderProcess(): Boolean = CrashReportService.isProperServiceProcess() +fun isAcraSenderProcess(): Boolean = AcraCrashReporter.isProperServiceProcess() + +@VisibleForTesting(otherwise = VisibleForTesting.NONE) +val CrashReportService.acraCoreConfigBuilder: CoreConfigurationBuilder + get() = AcraCrashReporter.acraCoreConfigBuilder + +@VisibleForTesting(otherwise = VisibleForTesting.NONE) +fun setDebugACRAConfig(sharedPrefs: SharedPreferences) = AcraCrashReporter.setDebugACRAConfig(sharedPrefs) + +@VisibleForTesting(otherwise = VisibleForTesting.NONE) +fun setProductionACRAConfig(sharedPrefs: SharedPreferences) = AcraCrashReporter.setProductionACRAConfig(sharedPrefs) diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt index d8b2a806c923..34c6f74d136b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt @@ -23,6 +23,7 @@ import android.os.Bundle import android.view.View import androidx.core.content.edit import com.ichi2.anki.CrashReportService +import com.ichi2.anki.CrashReporter import com.ichi2.anki.R import com.ichi2.anki.databinding.DialogFeedbackBinding import com.ichi2.anki.preferences.sharedPrefs @@ -87,11 +88,11 @@ class AnkiDroidCrashReportDialog : if (autoReport) { preferences.edit { putString( - CrashReportService.FEEDBACK_REPORT_KEY, - CrashReportService.FEEDBACK_REPORT_ALWAYS, + CrashReporter.FEEDBACK_REPORT_KEY, + CrashReporter.FEEDBACK_REPORT_ALWAYS, ) } - CrashReportService.setAcraReportingMode(CrashReportService.FEEDBACK_REPORT_ALWAYS) + CrashReportService.setReportingMode(CrashReporter.FEEDBACK_REPORT_ALWAYS) } // Send the crash report helper!!.sendCrash(binding.userComment.text.toString(), "") @@ -100,7 +101,7 @@ class AnkiDroidCrashReportDialog : // The limiter persists it's limit info *before* the user cancels. // Therefore, on cancel, purge limits to make sure the user may actually send in future. // Better to maybe send to many reports than definitely too few. - CrashReportService.deleteACRALimiterData(this) + CrashReportService.deleteLimiterData(this) helper!!.cancelReports() } finish() diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/DeveloperOptionsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/DeveloperOptionsFragment.kt index 0a4f16c70bfb..1bb637213510 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/DeveloperOptionsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/DeveloperOptionsFragment.kt @@ -60,7 +60,7 @@ class DeveloperOptionsFragment : SettingsFragment() { requirePreference(R.string.pref_trigger_crash_key).setOnPreferenceClickListener { // If we don't delete the limiter data, our test crash may not go through, // but we are triggering it very much on purpose, we want to see the crash in ACRA - this.context?.let { c -> CrashReportService.deleteACRALimiterData(c) } + this.context?.let { c -> CrashReportService.deleteLimiterData(c) } Timber.w("Crash triggered on purpose from advanced preferences in debug mode") throw RuntimeException("This is a test crash") diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/DebugInfoService.kt b/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/DebugInfoService.kt index a58416247cb5..7d9419214a15 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/DebugInfoService.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/DebugInfoService.kt @@ -53,7 +53,7 @@ object DebugInfoService { .replace("\n", " \n") } - private fun isSendingCrashReports(context: Context): Boolean = CrashReportService.isAcraEnabled(context, false) + private fun isSendingCrashReports(context: Context): Boolean = CrashReportService.isEnabled(context, false) } /** diff --git a/common/android/src/main/java/com/ichi2/anki/CrashReportService.kt b/common/android/src/main/java/com/ichi2/anki/CrashReportService.kt new file mode 100644 index 000000000000..3a8daff2211d --- /dev/null +++ b/common/android/src/main/java/com/ichi2/anki/CrashReportService.kt @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2022 lukstbit + * Copyright (c) 2026 Ashish Yadav + * + * 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 . + */ +package com.ichi2.anki + +import android.app.Activity +import android.content.Context +import androidx.annotation.VisibleForTesting +import timber.log.Timber + +/** + * Interface for crash/exception reporting. + * + * Implemented in the app module by the ACRA-backed crash reporter. + * + * TODO: Remove Context/Activity parameters from this interface and use the + * application context set during initialization instead. Context is only used + * for SharedPreferences and LimiterData, so an Application context is likely + * sufficient. Removing them enables use from java-library modules. + * Note: profile-aware contexts may affect this decision. + */ +interface CrashReporter { + fun sendExceptionReport( + message: String?, + origin: String?, + ) + + fun sendExceptionReport( + e: Throwable, + origin: String?, + additionalInfo: String? = null, + onlyIfSilent: Boolean = false, + ) + + fun sendExceptionReport( + e: Throwable, + origin: String?, + additionalInfo: String? = null, + onlyIfSilent: Boolean = false, + context: Context, + ) + + fun onPreferenceChanged( + ctx: Context, + newValue: String, + ) + + fun deleteLimiterData(context: Context) + + fun setReportingMode(value: String) + + fun isEnabled( + context: Context, + defaultValue: Boolean, + ): Boolean + + fun sendReport(activity: Activity): Boolean + + companion object { + const val FEEDBACK_REPORT_KEY = "reportErrorMode" + const val FEEDBACK_REPORT_ASK = "2" + const val FEEDBACK_REPORT_NEVER = "1" + const val FEEDBACK_REPORT_ALWAYS = "0" + } +} + +/** + * Global crash reporting service. Delegates to the [CrashReporter] implementation + * set during app initialization. + * + * Usage: + * ``` + * CrashReportService.sendExceptionReport(exception, "MyClass.myMethod") + * ``` + */ +object CrashReportService { + lateinit var instance: CrashReporter + private set + + fun setReporter(reporter: CrashReporter) { + instance = reporter + } + + @VisibleForTesting + fun getReporter(): CrashReporter = instance + + /** + * Reports a non-fatal issue without a [Throwable]. + * @param message Description of what went wrong. + * @param origin The class or method name where the issue occurred. + */ + fun sendExceptionReport( + message: String?, + origin: String?, + ) = instance.sendExceptionReport(message, origin) + + // TODO: remove these from instance and handle the conversion + // of parameters in this class/an extension method + + /** + * Reports a caught [Throwable] to the crash reporting service. + * @param e The exception or error to report. + * @param origin The tag or location where it occurred. + * @param additionalInfo Optional extra metadata to include in the report. + * @param onlyIfSilent If true, only sends the report if the user has opted into automatic reporting. + */ + fun sendExceptionReport( + e: Throwable, + origin: String?, + additionalInfo: String? = null, + onlyIfSilent: Boolean = false, + ) = instance.sendExceptionReport(e, origin, additionalInfo, onlyIfSilent) + + /** + * Reports a caught [Throwable] with a specific [Context]. + * Use this when the application context is insufficient + */ + fun sendExceptionReport( + e: Throwable, + origin: String?, + additionalInfo: String? = null, + onlyIfSilent: Boolean = false, + context: Context, + ) = instance.sendExceptionReport(e, origin, additionalInfo, onlyIfSilent, context) + + /** + * Manually triggers a crash report, often used for user-initiated feedback. + * @return True if the report was successfully queued; false if rate-limited. + */ + fun sendReport(activity: Activity): Boolean = instance.sendReport(activity) + + fun onPreferenceChanged( + ctx: Context, + newValue: String, + ) = instance.onPreferenceChanged(ctx, newValue) + + fun deleteLimiterData(context: Context) = instance.deleteLimiterData(context) + + fun setReportingMode(value: String) = instance.setReportingMode(value) + + fun isEnabled( + context: Context, + defaultValue: Boolean, + ): Boolean = instance.isEnabled(context, defaultValue) +} + +/** + * Runs the provided block, catching [Exception], logging it and reporting it to [CrashReportService] + * + * **Example** + * ``` + * runCatchingWithReport("callingMethod", onlyIfSilent = true) { + * doSomethingRisky() + * } + * ``` + * + * **Note**: This differs from [runCatching] - `Error` is thrown + * + * @param origin Data logged to Timber, and provided as the 'origin' field in the error report + * @param onlyIfSilent Skip crash report if the crash reporting service is not 'always accept' + * @param block Code to execute + * + * @throws Error If raised, this will be reported and rethrown + * + * @return A Result containing either the successful result of [block] or the [Exception] thrown + */ +fun runCatchingWithReport( + origin: String?, + onlyIfSilent: Boolean = false, + block: () -> T, +): Result = + try { + Result.success(block()) + } catch (e: Throwable) { + Timber.w(e, origin) + CrashReportService.sendExceptionReport(e, origin, onlyIfSilent = onlyIfSilent) + if (e is Error) throw e + Result.failure(e) + } From a38a6579f83c592d36d1589c176eac8a9e63f570 Mon Sep 17 00:00:00 2001 From: Ashish Yadav <48384865+criticalAY@users.noreply.github.com> Date: Sat, 18 Apr 2026 00:01:36 +0530 Subject: [PATCH 3/3] refactor: move CrashReportService to crashreporting package Move CrashReportService from com.ichi2.anki to com.ichi2.anki.common.crashreporting and update imports. Assisted-by: Claude Opus 4 (1M context) --- .../androidTest/java/com/ichi2/anki/tests/ACRATest.kt | 8 ++++---- .../src/main/java/com/ichi2/anki/AcraCrashReporter.kt | 10 ++++++---- AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.kt | 1 + AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt | 2 +- .../src/main/java/com/ichi2/anki/CoroutineHelpers.kt | 1 + .../src/main/java/com/ichi2/anki/DayRolloverHandler.kt | 1 + AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt | 1 + .../src/main/java/com/ichi2/anki/InitialActivity.kt | 1 + .../src/main/java/com/ichi2/anki/MediaRegistration.kt | 1 + .../src/main/java/com/ichi2/anki/NoteEditorFragment.kt | 1 + AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt | 1 + .../java/com/ichi2/anki/SharedDecksDownloadFragment.kt | 1 + .../main/java/com/ichi2/anki/StudyOptionsFragment.kt | 1 + .../ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt | 4 ++-- .../main/java/com/ichi2/anki/backend/BackendDBUtils.kt | 2 +- .../com/ichi2/anki/browser/CardBrowserViewModel.kt | 2 +- .../java/com/ichi2/anki/dialogs/BackupPromptDialog.kt | 2 +- .../com/ichi2/anki/dialogs/TtsPlaybackErrorDialog.kt | 2 +- .../anki/dialogs/help/HelpItemActionsDispatcher.kt | 2 +- .../ichi2/anki/multimedia/AudioRecordingFragment.kt | 2 +- .../com/ichi2/anki/multimedia/AudioVideoFragment.kt | 2 +- .../com/ichi2/anki/multimedia/MultimediaFragment.kt | 2 +- .../java/com/ichi2/anki/multimedia/MultimediaUtils.kt | 2 +- .../java/com/ichi2/anki/multiprofile/ProfileManager.kt | 2 +- .../java/com/ichi2/anki/notetype/ManageNotetypes.kt | 2 +- .../java/com/ichi2/anki/observability/ChangeManager.kt | 2 +- .../src/main/java/com/ichi2/anki/pages/DeckOptions.kt | 2 +- .../main/java/com/ichi2/anki/pages/PageChromeClient.kt | 2 +- .../ichi2/anki/preferences/DeveloperOptionsFragment.kt | 2 +- .../ichi2/anki/preferences/GeneralSettingsFragment.kt | 2 +- .../com/ichi2/anki/preferences/SyncSettingsFragment.kt | 2 +- .../com/ichi2/anki/provider/CardContentProvider.kt | 2 +- .../anki/reviewreminders/ReviewRemindersDatabase.kt | 2 +- .../com/ichi2/anki/servicelayer/DebugInfoService.kt | 2 +- .../src/main/java/com/ichi2/anki/snackbar/Snackbars.kt | 2 +- .../src/main/java/com/ichi2/anki/utils/Resources.kt | 2 +- .../main/java/com/ichi2/anki/utils/ShortcutUtils.kt | 2 +- .../anki/workarounds/AppLoadedFromBackupWorkaround.kt | 2 +- .../com/ichi2/anki/workarounds/SafeWebViewLayout.kt | 2 +- .../ichi2/compat/customtabs/CustomTabActivityHelper.kt | 2 +- .../com/ichi2/compat/customtabs/CustomTabsFallback.kt | 2 +- .../com/ichi2/compat/customtabs/CustomTabsHelper.kt | 2 +- AnkiDroid/src/main/java/com/ichi2/utils/BitmapUtil.kt | 2 +- .../src/main/java/com/ichi2/utils/ExceptionUtil.kt | 2 +- AnkiDroid/src/main/java/com/ichi2/utils/ImportUtils.kt | 2 +- .../src/main/java/com/ichi2/utils/VersionUtils.kt | 2 +- .../src/main/java/com/ichi2/utils/WebViewUtils.kt | 2 +- .../ichi2/widget/cardanalysis/CardAnalysisWidget.kt | 2 +- .../com/ichi2/widget/deckpicker/DeckPickerWidget.kt | 2 +- .../src/test/java/com/ichi2/anki/AnkiDroidAppTest.kt | 2 +- .../{ => common/crashreporting}/CrashReportService.kt | 2 +- 51 files changed, 60 insertions(+), 48 deletions(-) rename common/android/src/main/java/com/ichi2/anki/{ => common/crashreporting}/CrashReportService.kt (99%) diff --git a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt index 3cb0d6a51ded..6acb91e82614 100644 --- a/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt +++ b/AnkiDroid/src/androidTest/java/com/ichi2/anki/tests/ACRATest.kt @@ -22,13 +22,13 @@ import androidx.core.content.edit import androidx.test.annotation.UiThreadTest import androidx.test.ext.junit.runners.AndroidJUnit4 import com.ichi2.anki.AnkiDroidApp -import com.ichi2.anki.CrashReportService -import com.ichi2.anki.CrashReporter -import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_ALWAYS -import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_ASK import com.ichi2.anki.R import com.ichi2.anki.acraCoreConfigBuilder import com.ichi2.anki.analytics.UsageAnalytics +import com.ichi2.anki.common.crashreporting.CrashReportService +import com.ichi2.anki.common.crashreporting.CrashReporter +import com.ichi2.anki.common.crashreporting.CrashReporter.Companion.FEEDBACK_REPORT_ALWAYS +import com.ichi2.anki.common.crashreporting.CrashReporter.Companion.FEEDBACK_REPORT_ASK import com.ichi2.anki.logging.ProductionCrashReportingTree import com.ichi2.anki.preferences.sharedPrefs import com.ichi2.anki.servicelayer.ThrowableFilterService diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AcraCrashReporter.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AcraCrashReporter.kt index 982a8c2843e4..d8aa1f045fb4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AcraCrashReporter.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AcraCrashReporter.kt @@ -23,13 +23,15 @@ import androidx.annotation.VisibleForTesting import androidx.core.content.edit import androidx.core.content.pm.PackageInfoCompat import androidx.webkit.WebViewCompat -import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_ALWAYS -import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_ASK -import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_KEY -import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_NEVER import com.ichi2.anki.analytics.AnkiDroidCrashReportDialog import com.ichi2.anki.analytics.UsageAnalytics import com.ichi2.anki.analytics.UsageAnalytics.sendAnalyticsException +import com.ichi2.anki.common.crashreporting.CrashReportService +import com.ichi2.anki.common.crashreporting.CrashReporter +import com.ichi2.anki.common.crashreporting.CrashReporter.Companion.FEEDBACK_REPORT_ALWAYS +import com.ichi2.anki.common.crashreporting.CrashReporter.Companion.FEEDBACK_REPORT_ASK +import com.ichi2.anki.common.crashreporting.CrashReporter.Companion.FEEDBACK_REPORT_KEY +import com.ichi2.anki.common.crashreporting.CrashReporter.Companion.FEEDBACK_REPORT_NEVER import com.ichi2.anki.common.time.TimeManager import com.ichi2.anki.exception.ManuallyReportedException import com.ichi2.anki.exception.UserSubmittedException diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.kt index 43fca6a427e9..6e8d3e195daa 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.kt @@ -62,6 +62,7 @@ import com.ichi2.anki.android.input.ShortcutGroup import com.ichi2.anki.android.input.ShortcutGroupProvider import com.ichi2.anki.android.input.shortcut import com.ichi2.anki.common.annotations.LegacyNotifications +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.utils.annotation.KotlinCleanup import com.ichi2.anki.compat.CompatHelper import com.ichi2.anki.compat.CompatHelper.Companion.registerReceiverCompat diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt index 1f29df434c84..6f03e667c082 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt @@ -35,11 +35,11 @@ import androidx.fragment.app.FragmentActivity import androidx.lifecycle.MutableLiveData import anki.collection.OpChanges import com.ichi2.anki.AnkiDroidApp.Companion.sharedPreferencesTestingOverride -import com.ichi2.anki.CrashReportService.sendExceptionReport import com.ichi2.anki.analytics.UsageAnalytics import com.ichi2.anki.browser.SharedPreferencesLastDeckIdRepository import com.ichi2.anki.common.annotations.LegacyNotifications import com.ichi2.anki.common.annotations.NeedsTest +import com.ichi2.anki.common.crashreporting.CrashReportService.sendExceptionReport import com.ichi2.anki.common.utils.annotation.KotlinCleanup import com.ichi2.anki.compat.CompatHelper import com.ichi2.anki.contextmenu.AnkiCardContextMenu diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt b/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt index ec3c06612208..979ecd5a38ec 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/CoroutineHelpers.kt @@ -42,6 +42,7 @@ import com.ichi2.anki.CrashReportData.HelpAction.AnkiBackendLink import com.ichi2.anki.CrashReportData.HelpAction.OpenDeckOptions import com.ichi2.anki.android.AnkiBroadcastReceiver import com.ichi2.anki.common.annotations.UseContextParameter +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.dialogs.DatabaseErrorDialog import com.ichi2.anki.dialogs.DatabaseErrorDialog.DatabaseErrorDialogType import com.ichi2.anki.exception.StorageAccessException diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DayRolloverHandler.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DayRolloverHandler.kt index b0a6be526db4..ac047442b381 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/DayRolloverHandler.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/DayRolloverHandler.kt @@ -35,6 +35,7 @@ import anki.collection.OpChanges import anki.collection.opChanges import com.ichi2.anki.CollectionManager.withOpenColOrNull import com.ichi2.anki.android.AnkiBroadcastReceiver +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.exception.ManuallyReportedException import com.ichi2.anki.libanki.EpochSeconds import com.ichi2.anki.libanki.sched.Scheduler diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt index 43e573d94f18..da809c281452 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt @@ -93,6 +93,7 @@ import com.ichi2.anki.android.back.exitViaDoubleTapBackCallback import com.ichi2.anki.android.input.ShortcutGroup import com.ichi2.anki.android.input.shortcut import com.ichi2.anki.common.annotations.NeedsTest +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.time.TimeManager import com.ichi2.anki.common.utils.annotation.KotlinCleanup import com.ichi2.anki.compat.CompatHelper.Companion.getSerializableCompat diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/InitialActivity.kt b/AnkiDroid/src/main/java/com/ichi2/anki/InitialActivity.kt index c05484e451f4..1042fc7312b3 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/InitialActivity.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/InitialActivity.kt @@ -27,6 +27,7 @@ import android.os.Parcelable import androidx.annotation.CheckResult import androidx.annotation.RequiresApi import androidx.core.content.edit +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.compat.CompatHelper.Companion.sdkVersion import com.ichi2.anki.dialogs.DatabaseErrorDialog import com.ichi2.anki.exception.StorageAccessException diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/MediaRegistration.kt b/AnkiDroid/src/main/java/com/ichi2/anki/MediaRegistration.kt index 254d9e067adf..256da728b3a9 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/MediaRegistration.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/MediaRegistration.kt @@ -22,6 +22,7 @@ import android.graphics.BitmapFactory import android.net.Uri import androidx.annotation.CheckResult import com.ichi2.anki.common.annotations.NeedsTest +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.compat.CompatHelper import com.ichi2.anki.libanki.exception.EmptyMediaException import com.ichi2.anki.multimediacard.fields.ImageField diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt index d968c1df14e2..6a9817e6beec 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/NoteEditorFragment.kt @@ -90,6 +90,7 @@ import com.ichi2.anki.android.input.ShortcutGroup import com.ichi2.anki.android.input.ShortcutGroupProvider import com.ichi2.anki.android.input.shortcut import com.ichi2.anki.common.annotations.NeedsTest +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.utils.annotation.KotlinCleanup import com.ichi2.anki.common.utils.ext.ifZero import com.ichi2.anki.compat.CompatHelper.Companion.getSerializableCompat diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt index 24d14780d2ca..6fafd27f5b9d 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/Reviewer.kt @@ -69,6 +69,7 @@ import com.ichi2.anki.Whiteboard.OnPaintColorChangeListener import com.ichi2.anki.cardviewer.Gesture import com.ichi2.anki.cardviewer.ViewerCommand import com.ichi2.anki.common.annotations.NeedsTest +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.time.TimeManager import com.ichi2.anki.libanki.Card import com.ichi2.anki.libanki.CardId diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/SharedDecksDownloadFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/SharedDecksDownloadFragment.kt index fa13bc77b434..610f8ed08799 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/SharedDecksDownloadFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/SharedDecksDownloadFragment.kt @@ -38,6 +38,7 @@ import androidx.fragment.app.Fragment import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.SharedDecksActivity.Companion.DOWNLOAD_FILE import com.ichi2.anki.android.AnkiBroadcastReceiver +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.compat.CompatHelper.Companion.getSerializableCompat import com.ichi2.anki.compat.CompatHelper.Companion.registerReceiverCompat import com.ichi2.anki.databinding.FragmentSharedDecksDownloadBinding diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt index 444812e83b66..14fa6d89a753 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/StudyOptionsFragment.kt @@ -42,6 +42,7 @@ import anki.collection.OpChanges import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol import com.ichi2.anki.backend.stripHTMLScriptAndStyleTags +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.dialogs.customstudy.CustomStudyDialog import com.ichi2.anki.filtered.FilteredDeckOptionsFragment import com.ichi2.anki.libanki.Collection diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt index 34c6f74d136b..6680ad656765 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/analytics/AnkiDroidCrashReportDialog.kt @@ -22,9 +22,9 @@ import android.content.DialogInterface import android.os.Bundle import android.view.View import androidx.core.content.edit -import com.ichi2.anki.CrashReportService -import com.ichi2.anki.CrashReporter import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.CrashReportService +import com.ichi2.anki.common.crashreporting.CrashReporter import com.ichi2.anki.databinding.DialogFeedbackBinding import com.ichi2.anki.preferences.sharedPrefs import org.acra.dialog.CrashReportDialog diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/backend/BackendDBUtils.kt b/AnkiDroid/src/main/java/com/ichi2/anki/backend/BackendDBUtils.kt index ed8d3befe2bd..89c99db74b41 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/backend/BackendDBUtils.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/backend/BackendDBUtils.kt @@ -20,7 +20,7 @@ import android.content.Context import androidx.annotation.CheckResult import androidx.sqlite.db.SupportSQLiteDatabase import com.ichi2.anki.CollectionManager -import com.ichi2.anki.CrashReportService.sendExceptionReport +import com.ichi2.anki.common.crashreporting.CrashReportService.sendExceptionReport import com.ichi2.anki.dialogs.DatabaseErrorDialog import com.ichi2.anki.libanki.DB import net.ankiweb.rsdroid.Backend diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserViewModel.kt b/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserViewModel.kt index fb3de3c95d31..bd7fa673ec39 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserViewModel.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/browser/CardBrowserViewModel.kt @@ -38,7 +38,6 @@ import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol -import com.ichi2.anki.CrashReportService import com.ichi2.anki.Flag import com.ichi2.anki.PreviewerDestination import com.ichi2.anki.browser.CardBrowserViewModel.ChangeMultiSelectMode.MultiSelectCause @@ -54,6 +53,7 @@ import com.ichi2.anki.browser.search.SearchFilters import com.ichi2.anki.browser.search.SearchRequest import com.ichi2.anki.browser.search.SearchString import com.ichi2.anki.common.annotations.NeedsTest +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.utils.ext.indexOfOrNull import com.ichi2.anki.export.ExportDialogFragment.ExportType import com.ichi2.anki.launchCatchingIO diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/BackupPromptDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/BackupPromptDialog.kt index 9304ba04f8d3..ff50611f3ed0 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/BackupPromptDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/BackupPromptDialog.kt @@ -22,9 +22,9 @@ import androidx.annotation.StringRes import androidx.appcompat.app.AlertDialog import androidx.core.content.edit import com.ichi2.anki.CollectionManager.withCol -import com.ichi2.anki.CrashReportService import com.ichi2.anki.DeckPicker import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.time.TimeManager import com.ichi2.anki.compat.CompatHelper.Companion.getPackageInfoCompat import com.ichi2.anki.compat.PackageInfoFlagsCompat diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/TtsPlaybackErrorDialog.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/TtsPlaybackErrorDialog.kt index 160ec0f5c4c3..00146fa4c9a4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/TtsPlaybackErrorDialog.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/TtsPlaybackErrorDialog.kt @@ -20,9 +20,9 @@ import android.app.Activity import android.content.Intent import androidx.appcompat.app.AlertDialog import androidx.fragment.app.FragmentManager -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R import com.ichi2.anki.TtsVoices +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.libanki.TTSTag import com.ichi2.anki.utils.openUrl import com.ichi2.utils.show diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/help/HelpItemActionsDispatcher.kt b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/help/HelpItemActionsDispatcher.kt index 79cc60dac60c..100a0c9e0ea4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/help/HelpItemActionsDispatcher.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/help/HelpItemActionsDispatcher.kt @@ -16,8 +16,8 @@ package com.ichi2.anki.dialogs.help import androidx.annotation.StringRes import com.ichi2.anki.AnkiActivity import com.ichi2.anki.AnkiDroidApp -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.snackbar.showSnackbar import com.ichi2.utils.AdaptionUtil import com.ichi2.utils.IntentUtil diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioRecordingFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioRecordingFragment.kt index a71e430903a1..3c5f1d7c92ae 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioRecordingFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioRecordingFragment.kt @@ -25,9 +25,9 @@ import android.view.View import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R import com.ichi2.anki.common.annotations.NeedsTest +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.databinding.FragmentAudioRecordingBinding import com.ichi2.anki.multimedia.MultimediaActivity.Companion.MULTIMEDIA_RESULT import com.ichi2.anki.multimedia.MultimediaActivity.Companion.MULTIMEDIA_RESULT_FIELD_INDEX diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioVideoFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioVideoFragment.kt index d944319197aa..5eafb517c3e1 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioVideoFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/AudioVideoFragment.kt @@ -34,9 +34,9 @@ import androidx.media3.common.C import androidx.media3.common.MediaItem import androidx.media3.common.util.UnstableApi import androidx.media3.exoplayer.ExoPlayer -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R import com.ichi2.anki.common.annotations.NeedsTest +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.compat.CompatHelper import com.ichi2.anki.compat.CompatHelper.Companion.getSerializableCompat import com.ichi2.anki.databinding.FragmentAudioVideoBinding diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaFragment.kt index 88d270385e06..213878dc6604 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaFragment.kt @@ -35,9 +35,9 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import com.ichi2.anki.CollectionManager.TR -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R import com.ichi2.anki.common.annotations.NeedsTest +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.compat.CompatHelper.Companion.getSerializableCompat import com.ichi2.anki.dialogs.DiscardChangesDialog import com.ichi2.anki.multimediacard.IMultimediaEditableNote diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaUtils.kt b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaUtils.kt index e8d909b13d8e..cf8eb7b67764 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaUtils.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/multimedia/MultimediaUtils.kt @@ -26,7 +26,7 @@ import android.provider.DocumentsContract import android.provider.MediaStore import androidx.core.content.ContentResolverCompat import androidx.core.net.toUri -import com.ichi2.anki.CrashReportService +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.time.TimeManager import com.ichi2.anki.common.time.getTimestamp import timber.log.Timber diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/multiprofile/ProfileManager.kt b/AnkiDroid/src/main/java/com/ichi2/anki/multiprofile/ProfileManager.kt index 61db266ba782..7ab079fc0773 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/multiprofile/ProfileManager.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/multiprofile/ProfileManager.kt @@ -26,8 +26,8 @@ import android.webkit.WebView import androidx.annotation.VisibleForTesting import androidx.core.content.ContextCompat import androidx.core.content.edit -import com.ichi2.anki.CrashReportService import com.ichi2.anki.IntentHandler +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.time.TimeManager import com.ichi2.anki.common.time.getTimestamp import org.acra.ACRA diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/notetype/ManageNotetypes.kt b/AnkiDroid/src/main/java/com/ichi2/anki/notetype/ManageNotetypes.kt index bb4e70ab9520..ee7ff7faa5a4 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/notetype/ManageNotetypes.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/notetype/ManageNotetypes.kt @@ -35,8 +35,8 @@ import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import com.ichi2.anki.AnkiActivity import com.ichi2.anki.CollectionManager.TR -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.databinding.ActivityManageNoteTypesBinding import com.ichi2.anki.dialogs.dismissLoadingDialog import com.ichi2.anki.dialogs.showLoadingDialog diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/observability/ChangeManager.kt b/AnkiDroid/src/main/java/com/ichi2/anki/observability/ChangeManager.kt index e5def5d6db96..9ce35bdc2338 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/observability/ChangeManager.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/observability/ChangeManager.kt @@ -38,7 +38,7 @@ import anki.collection.OpChangesWithCount import anki.collection.OpChangesWithId import anki.collection.opChanges import anki.import_export.ImportResponse -import com.ichi2.anki.CrashReportService +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.utils.ext.ifNotZero import org.jetbrains.annotations.Contract import timber.log.Timber diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/DeckOptions.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/DeckOptions.kt index d1f4b4466ada..2cb1414c153c 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/DeckOptions.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/DeckOptions.kt @@ -31,11 +31,11 @@ import anki.collection.Progress import com.google.android.material.appbar.MaterialToolbar import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol -import com.ichi2.anki.CrashReportService import com.ichi2.anki.ProgressContext import com.ichi2.anki.R import com.ichi2.anki.SingleFragmentActivity import com.ichi2.anki.common.annotations.NeedsTest +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.launchCatchingTask import com.ichi2.anki.libanki.DeckId import com.ichi2.anki.libanki.updateDeckConfigsRaw diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/pages/PageChromeClient.kt b/AnkiDroid/src/main/java/com/ichi2/anki/pages/PageChromeClient.kt index 68e1056f4ecd..ff4f6d3ca11d 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/pages/PageChromeClient.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/pages/PageChromeClient.kt @@ -20,8 +20,8 @@ import android.webkit.JsResult import android.webkit.WebChromeClient import android.webkit.WebView import androidx.appcompat.app.AlertDialog -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.utils.cancelable import com.ichi2.utils.message import com.ichi2.utils.negativeButton diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/DeveloperOptionsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/DeveloperOptionsFragment.kt index 1bb637213510..60770251d342 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/DeveloperOptionsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/DeveloperOptionsFragment.kt @@ -24,9 +24,9 @@ import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.BuildConfig import com.ichi2.anki.CollectionHelper import com.ichi2.anki.CollectionManager.withCol -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R import com.ichi2.anki.analytics.UsageAnalytics +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.dialogs.TtsVoicesDialogFragment import com.ichi2.anki.launchCatchingTask import com.ichi2.anki.settings.Prefs diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/GeneralSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/GeneralSettingsFragment.kt index de4f79409b1d..d38543cf6108 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/GeneralSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/GeneralSettingsFragment.kt @@ -22,8 +22,8 @@ import androidx.preference.SwitchPreferenceCompat import anki.config.ConfigKey import com.ichi2.anki.CollectionManager import com.ichi2.anki.CollectionManager.withCol -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.contextmenu.AnkiCardContextMenu import com.ichi2.anki.contextmenu.CardBrowserContextMenu import com.ichi2.anki.launchCatchingTask diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/SyncSettingsFragment.kt b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/SyncSettingsFragment.kt index ce67ce319109..ddb22810dc96 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/preferences/SyncSettingsFragment.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/preferences/SyncSettingsFragment.kt @@ -23,8 +23,8 @@ import com.ichi2.anki.CollectionManager.TR import com.ichi2.anki.CollectionManager.withCol import com.ichi2.anki.R import com.ichi2.anki.account.AccountActivity +import com.ichi2.anki.common.crashreporting.runCatchingWithReport import com.ichi2.anki.launchCatchingTask -import com.ichi2.anki.runCatchingWithReport import com.ichi2.anki.settings.Prefs import com.ichi2.anki.snackbar.showSnackbar import com.ichi2.anki.utils.ext.ifNullOrEmpty diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/provider/CardContentProvider.kt b/AnkiDroid/src/main/java/com/ichi2/anki/provider/CardContentProvider.kt index d2b09b7c01ee..5b825900bc65 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/provider/CardContentProvider.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/provider/CardContentProvider.kt @@ -34,8 +34,8 @@ import anki.scheduler.CardAnswer import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.BuildConfig import com.ichi2.anki.CollectionManager -import com.ichi2.anki.CrashReportService import com.ichi2.anki.FlashCardsContract +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.time.TimeManager import com.ichi2.anki.common.utils.annotation.KotlinCleanup import com.ichi2.anki.libanki.Card diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ReviewRemindersDatabase.kt b/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ReviewRemindersDatabase.kt index 869238328686..3f4baceb8b8f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ReviewRemindersDatabase.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/reviewreminders/ReviewRemindersDatabase.kt @@ -21,7 +21,7 @@ import android.content.SharedPreferences import androidx.annotation.VisibleForTesting import androidx.core.content.edit import com.ichi2.anki.AnkiDroidApp -import com.ichi2.anki.CrashReportService +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.libanki.DeckId import com.ichi2.anki.settings.Prefs import com.ichi2.anki.showError diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/DebugInfoService.kt b/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/DebugInfoService.kt index 7d9419214a15..259fae3be650 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/DebugInfoService.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/servicelayer/DebugInfoService.kt @@ -20,7 +20,7 @@ import android.content.Context import android.os.Build import com.ichi2.anki.BuildConfig import com.ichi2.anki.CollectionManager -import com.ichi2.anki.CrashReportService +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.utils.VersionUtils.pkgVersionName import com.ichi2.utils.getWebViewInfo import org.acra.util.Installation diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/snackbar/Snackbars.kt b/AnkiDroid/src/main/java/com/ichi2/anki/snackbar/Snackbars.kt index 97f767f3d5cf..06fba5c68ecd 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/snackbar/Snackbars.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/snackbar/Snackbars.kt @@ -27,8 +27,8 @@ import androidx.fragment.app.Fragment import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.onAttachedToWindow2 import com.ichi2.anki.BuildConfig -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.exception.ManuallyReportedException import com.ichi2.anki.showThemedToast import timber.log.Timber diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/utils/Resources.kt b/AnkiDroid/src/main/java/com/ichi2/anki/utils/Resources.kt index 532e5f75963a..79d56d54834b 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/utils/Resources.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/utils/Resources.kt @@ -21,8 +21,8 @@ import android.content.res.Resources import androidx.annotation.PluralsRes import androidx.annotation.StringRes import androidx.fragment.app.FragmentActivity -import com.ichi2.anki.CrashReportService import com.ichi2.anki.common.annotations.NeedsTest +import com.ichi2.anki.common.crashreporting.CrashReportService /** * @param resId must be a [StringRes] or a [PluralsRes] diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/utils/ShortcutUtils.kt b/AnkiDroid/src/main/java/com/ichi2/anki/utils/ShortcutUtils.kt index 2d150e7fba78..370fc04a765f 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/utils/ShortcutUtils.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/utils/ShortcutUtils.kt @@ -18,7 +18,7 @@ package com.ichi2.anki.utils import android.content.Context import androidx.core.content.pm.ShortcutManagerCompat -import com.ichi2.anki.runCatchingWithReport +import com.ichi2.anki.common.crashreporting.runCatchingWithReport import timber.log.Timber /** diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/workarounds/AppLoadedFromBackupWorkaround.kt b/AnkiDroid/src/main/java/com/ichi2/anki/workarounds/AppLoadedFromBackupWorkaround.kt index 8f5c205eeefd..ce3c4577e927 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/workarounds/AppLoadedFromBackupWorkaround.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/workarounds/AppLoadedFromBackupWorkaround.kt @@ -20,8 +20,8 @@ import android.app.Activity import android.os.Bundle import android.os.Process import com.ichi2.anki.AnkiDroidApp -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.exception.ManuallyReportedException import com.ichi2.anki.showThemedToast import com.ichi2.themes.Themes diff --git a/AnkiDroid/src/main/java/com/ichi2/anki/workarounds/SafeWebViewLayout.kt b/AnkiDroid/src/main/java/com/ichi2/anki/workarounds/SafeWebViewLayout.kt index 6a24a17e3d1f..f9633d63f8de 100644 --- a/AnkiDroid/src/main/java/com/ichi2/anki/workarounds/SafeWebViewLayout.kt +++ b/AnkiDroid/src/main/java/com/ichi2/anki/workarounds/SafeWebViewLayout.kt @@ -27,7 +27,7 @@ import androidx.annotation.MainThread import androidx.fragment.app.Fragment import androidx.fragment.app.findFragment import com.ichi2.anki.BuildConfig -import com.ichi2.anki.runCatchingWithReport +import com.ichi2.anki.common.crashreporting.runCatchingWithReport import timber.log.Timber open class SafeWebViewLayout : diff --git a/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabActivityHelper.kt b/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabActivityHelper.kt index ff3ee7b627d1..79cb571c3095 100644 --- a/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabActivityHelper.kt +++ b/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabActivityHelper.kt @@ -24,8 +24,8 @@ import androidx.browser.customtabs.CustomTabsClient import androidx.browser.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsServiceConnection import androidx.browser.customtabs.CustomTabsSession -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.snackbar.showSnackbar import timber.log.Timber diff --git a/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabsFallback.kt b/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabsFallback.kt index a1c15f8cf5f5..ee8bc4027adb 100644 --- a/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabsFallback.kt +++ b/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabsFallback.kt @@ -17,8 +17,8 @@ package com.ichi2.compat.customtabs import android.app.Activity import android.content.Intent import android.net.Uri -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.showThemedToast import com.ichi2.compat.customtabs.CustomTabActivityHelper.CustomTabFallback diff --git a/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabsHelper.kt b/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabsHelper.kt index b01aaa86c356..6ca320583338 100644 --- a/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabsHelper.kt +++ b/AnkiDroid/src/main/java/com/ichi2/compat/customtabs/CustomTabsHelper.kt @@ -19,12 +19,12 @@ import android.content.Intent import android.content.pm.PackageManager import androidx.browser.customtabs.CustomTabsService import androidx.core.net.toUri +import com.ichi2.anki.common.crashreporting.runCatchingWithReport import com.ichi2.anki.compat.CompatHelper.Companion.queryIntentActivitiesCompat import com.ichi2.anki.compat.CompatHelper.Companion.resolveActivityCompat import com.ichi2.anki.compat.CompatHelper.Companion.resolveServiceCompat import com.ichi2.anki.compat.GET_RESOLVED_FILTER_L import com.ichi2.anki.compat.ResolveInfoFlagsCompat -import com.ichi2.anki.runCatchingWithReport import timber.log.Timber /** diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/BitmapUtil.kt b/AnkiDroid/src/main/java/com/ichi2/utils/BitmapUtil.kt index 9cba3846ec48..d8fe08618eef 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/BitmapUtil.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/BitmapUtil.kt @@ -24,7 +24,7 @@ import android.graphics.BitmapFactory import android.graphics.drawable.BitmapDrawable import android.widget.ImageView import androidx.annotation.CheckResult -import com.ichi2.anki.CrashReportService +import com.ichi2.anki.common.crashreporting.CrashReportService import timber.log.Timber import java.io.File import java.io.FileInputStream diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/ExceptionUtil.kt b/AnkiDroid/src/main/java/com/ichi2/utils/ExceptionUtil.kt index 164236b28c71..058c04b8bcbf 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/ExceptionUtil.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/ExceptionUtil.kt @@ -17,8 +17,8 @@ package com.ichi2.utils import android.content.Context import androidx.annotation.CheckResult -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.showThemedToast import java.io.PrintWriter import java.io.StringWriter diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/ImportUtils.kt b/AnkiDroid/src/main/java/com/ichi2/utils/ImportUtils.kt index bf6732cad5fb..fb607f56b6db 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/ImportUtils.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/ImportUtils.kt @@ -29,9 +29,9 @@ import androidx.appcompat.app.AlertDialog import androidx.core.os.bundleOf import com.ichi2.anki.AnkiActivity import com.ichi2.anki.AnkiDroidApp -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R import com.ichi2.anki.common.annotations.NeedsTest +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.common.time.TimeManager import com.ichi2.anki.compat.CompatHelper import com.ichi2.anki.dialogs.DialogHandler diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/VersionUtils.kt b/AnkiDroid/src/main/java/com/ichi2/utils/VersionUtils.kt index d1e43e83519d..619d8dd014da 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/VersionUtils.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/VersionUtils.kt @@ -20,7 +20,7 @@ import android.content.Context import android.content.pm.PackageManager import androidx.core.content.pm.PackageInfoCompat import com.ichi2.anki.AnkiDroidApp -import com.ichi2.anki.CrashReportService +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.compat.CompatHelper.Companion.getPackageInfoCompat import com.ichi2.anki.compat.PackageInfoFlagsCompat import timber.log.Timber diff --git a/AnkiDroid/src/main/java/com/ichi2/utils/WebViewUtils.kt b/AnkiDroid/src/main/java/com/ichi2/utils/WebViewUtils.kt index caa565323734..4ce9af9d87e2 100644 --- a/AnkiDroid/src/main/java/com/ichi2/utils/WebViewUtils.kt +++ b/AnkiDroid/src/main/java/com/ichi2/utils/WebViewUtils.kt @@ -28,8 +28,8 @@ import androidx.appcompat.app.AlertDialog import androidx.core.content.pm.PackageInfoCompat import androidx.webkit.WebViewCompat import com.ichi2.anki.AnkiActivity -import com.ichi2.anki.CrashReportService import com.ichi2.anki.R +import com.ichi2.anki.common.crashreporting.CrashReportService import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import timber.log.Timber diff --git a/AnkiDroid/src/main/java/com/ichi2/widget/cardanalysis/CardAnalysisWidget.kt b/AnkiDroid/src/main/java/com/ichi2/widget/cardanalysis/CardAnalysisWidget.kt index f678b357d09e..e7606b3163b7 100644 --- a/AnkiDroid/src/main/java/com/ichi2/widget/cardanalysis/CardAnalysisWidget.kt +++ b/AnkiDroid/src/main/java/com/ichi2/widget/cardanalysis/CardAnalysisWidget.kt @@ -25,10 +25,10 @@ import android.content.Intent import android.view.View import android.widget.RemoteViews import com.ichi2.anki.AnkiDroidApp -import com.ichi2.anki.CrashReportService import com.ichi2.anki.IntentHandler.Companion.intentToReviewDeckFromShortcuts import com.ichi2.anki.R import com.ichi2.anki.analytics.UsageAnalytics +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.isCollectionEmpty import com.ichi2.anki.libanki.DeckId import com.ichi2.anki.libanki.Decks.Companion.NOT_FOUND_DECK_ID diff --git a/AnkiDroid/src/main/java/com/ichi2/widget/deckpicker/DeckPickerWidget.kt b/AnkiDroid/src/main/java/com/ichi2/widget/deckpicker/DeckPickerWidget.kt index a10a54f150b3..a42d1595867d 100644 --- a/AnkiDroid/src/main/java/com/ichi2/widget/deckpicker/DeckPickerWidget.kt +++ b/AnkiDroid/src/main/java/com/ichi2/widget/deckpicker/DeckPickerWidget.kt @@ -26,10 +26,10 @@ import android.view.View import android.widget.RemoteViews import com.ichi2.anki.AnkiDroidApp import com.ichi2.anki.CollectionManager.withCol -import com.ichi2.anki.CrashReportService import com.ichi2.anki.IntentHandler.Companion.intentToReviewDeckFromShortcuts import com.ichi2.anki.R import com.ichi2.anki.analytics.UsageAnalytics +import com.ichi2.anki.common.crashreporting.CrashReportService import com.ichi2.anki.isCollectionEmpty import com.ichi2.anki.libanki.DeckId import com.ichi2.anki.pages.DeckOptionsDestination diff --git a/AnkiDroid/src/test/java/com/ichi2/anki/AnkiDroidAppTest.kt b/AnkiDroid/src/test/java/com/ichi2/anki/AnkiDroidAppTest.kt index fe701ed0597b..19868f25fc14 100644 --- a/AnkiDroid/src/test/java/com/ichi2/anki/AnkiDroidAppTest.kt +++ b/AnkiDroid/src/test/java/com/ichi2/anki/AnkiDroidAppTest.kt @@ -16,7 +16,7 @@ package com.ichi2.anki import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.ichi2.anki.CrashReportService.sendExceptionReport +import com.ichi2.anki.common.crashreporting.CrashReportService.sendExceptionReport import org.junit.Test import org.junit.jupiter.api.assertDoesNotThrow import org.junit.runner.RunWith diff --git a/common/android/src/main/java/com/ichi2/anki/CrashReportService.kt b/common/android/src/main/java/com/ichi2/anki/common/crashreporting/CrashReportService.kt similarity index 99% rename from common/android/src/main/java/com/ichi2/anki/CrashReportService.kt rename to common/android/src/main/java/com/ichi2/anki/common/crashreporting/CrashReportService.kt index 3a8daff2211d..7d427fdd5f2f 100644 --- a/common/android/src/main/java/com/ichi2/anki/CrashReportService.kt +++ b/common/android/src/main/java/com/ichi2/anki/common/crashreporting/CrashReportService.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ -package com.ichi2.anki +package com.ichi2.anki.common.crashreporting import android.app.Activity import android.content.Context