@@ -23,6 +23,10 @@ import androidx.annotation.VisibleForTesting
2323import androidx.core.content.edit
2424import androidx.core.content.pm.PackageInfoCompat
2525import androidx.webkit.WebViewCompat
26+ import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_ALWAYS
27+ import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_ASK
28+ import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_KEY
29+ import com.ichi2.anki.CrashReporter.Companion.FEEDBACK_REPORT_NEVER
2630import com.ichi2.anki.analytics.AnkiDroidCrashReportDialog
2731import com.ichi2.anki.analytics.UsageAnalytics
2832import com.ichi2.anki.analytics.UsageAnalytics.sendAnalyticsException
@@ -43,13 +47,7 @@ import org.acra.config.ToastConfigurationBuilder
4347import org.acra.sender.HttpSender
4448import timber.log.Timber
4549
46- object CrashReportService {
47- // ACRA constants used for stored preferences
48- const val FEEDBACK_REPORT_KEY = " reportErrorMode"
49- const val FEEDBACK_REPORT_ASK = " 2"
50- const val FEEDBACK_REPORT_NEVER = " 1"
51- const val FEEDBACK_REPORT_ALWAYS = " 0"
52-
50+ private object AcraCrashReporter : CrashReporter {
5351 /* * Our ACRA configurations, initialized during Application.onCreate() */
5452 @JvmStatic
5553 private var logcatArgs =
@@ -166,15 +164,15 @@ object CrashReportService {
166164 */
167165 @JvmStatic
168166 fun initialize (application : Application ) {
169- CrashReportService .application = application
167+ this .application = application
170168 // FIXME ACRA needs to reinitialize after language is changed, but with the new language
171169 // this is difficult because the Application (AnkiDroidApp) does not change it's baseContext
172170 // perhaps a solution could be to change AnkiDroidApp to have a context wrapper that it sets
173171 // as baseContext, and that wrapper allows a resources/configuration update, then
174172 // in GeneralSettingsFragment for the language dialog change listener, the context wrapper
175173 // could be updated directly with the new locale code so that calling getString on would fetch
176174 // the new language string ?
177- toastText = ToastType .AUTO_TOAST .getToastMessage(CrashReportService . application)
175+ toastText = ToastType .AUTO_TOAST .getToastMessage(application)
178176
179177 // Setup logging and crash reporting
180178 if (BuildConfig .DEBUG ) {
@@ -195,7 +193,7 @@ object CrashReportService {
195193 * Set the reporting mode for ACRA based on the value of the FEEDBACK_REPORT_KEY preference
196194 * @param value value of FEEDBACK_REPORT_KEY preference
197195 */
198- fun setAcraReportingMode (value : String ) {
196+ override fun setReportingMode (value : String ) {
199197 application.sharedPrefs().edit {
200198 // Set the ACRA disable value
201199 if (value == FEEDBACK_REPORT_NEVER ) {
@@ -225,7 +223,7 @@ object CrashReportService {
225223 @VisibleForTesting(otherwise = VisibleForTesting .PRIVATE )
226224 fun setDebugACRAConfig (prefs : SharedPreferences ) {
227225 // Disable crash reporting
228- setAcraReportingMode (FEEDBACK_REPORT_NEVER )
226+ setReportingMode (FEEDBACK_REPORT_NEVER )
229227 prefs.edit { putString(FEEDBACK_REPORT_KEY , FEEDBACK_REPORT_NEVER ) }
230228 // Use a wider logcat filter in case crash reporting manually re-enabled
231229 logcatArgs = arrayOf(" -t" , " 1500" , " -v" , " long" , " ACRA:S" )
@@ -240,7 +238,7 @@ object CrashReportService {
240238 @VisibleForTesting(otherwise = VisibleForTesting .PRIVATE )
241239 fun setProductionACRAConfig (prefs : SharedPreferences ) {
242240 // Enable or disable crash reporting based on user setting
243- setAcraReportingMode (prefs.getString(FEEDBACK_REPORT_KEY , FEEDBACK_REPORT_ASK )!! )
241+ setReportingMode (prefs.getString(FEEDBACK_REPORT_KEY , FEEDBACK_REPORT_ASK )!! )
244242 }
245243
246244 private fun fetchWebViewInformation (): HashMap <String , String > {
@@ -262,17 +260,24 @@ object CrashReportService {
262260 }
263261
264262 /* * Used when we don't have an exception to throw, but we know something is wrong and want to diagnose it */
265- fun sendExceptionReport (
263+ override fun sendExceptionReport (
266264 message : String? ,
267265 origin : String? ,
268266 ) = sendExceptionReport(ManuallyReportedException (message), origin)
269267
270- fun sendExceptionReport (
268+ override fun sendExceptionReport (
271269 e : Throwable ,
272270 origin : String? ,
273- additionalInfo : String? = null,
274- onlyIfSilent : Boolean = false,
275- context : Context = application.applicationContext,
271+ additionalInfo : String? ,
272+ onlyIfSilent : Boolean ,
273+ ) = sendExceptionReport(e, origin, additionalInfo, onlyIfSilent, application.applicationContext)
274+
275+ override fun sendExceptionReport (
276+ e : Throwable ,
277+ origin : String? ,
278+ additionalInfo : String? ,
279+ onlyIfSilent : Boolean ,
280+ context : Context ,
276281 ) {
277282 sendAnalyticsException(e, false )
278283 AnkiDroidApp .sentExceptionReportHack = true
@@ -297,7 +302,7 @@ object CrashReportService {
297302
298303 fun isProperServiceProcess (): Boolean = ACRA .isACRASenderServiceProcess()
299304
300- fun isAcraEnabled (
305+ override fun isEnabled (
301306 context : Context ,
302307 defaultValue : Boolean ,
303308 ): Boolean {
@@ -314,21 +319,21 @@ object CrashReportService {
314319 *
315320 * @param context the context leading to the directory with ACRA limiter data
316321 */
317- fun deleteACRALimiterData (context : Context ) {
322+ override fun deleteLimiterData (context : Context ) {
318323 try {
319324 LimiterData ().store(context)
320325 } catch (e: Exception ) {
321326 Timber .w(e, " Unable to clear ACRA limiter data" )
322327 }
323328 }
324329
325- fun onPreferenceChanged (
330+ override fun onPreferenceChanged (
326331 ctx : Context ,
327332 newValue : String ,
328333 ) {
329- setAcraReportingMode (newValue)
334+ setReportingMode (newValue)
330335 // If the user changed error reporting, make sure future reports have a chance to post
331- deleteACRALimiterData (ctx)
336+ deleteLimiterData (ctx)
332337 // We also need to re-chain our UncaughtExceptionHandlers
333338 UsageAnalytics .reInitialize()
334339 ThrowableFilterService .reInitialize()
@@ -338,7 +343,8 @@ object CrashReportService {
338343 * @return the status of the report, true if the report was sent, false if the report is already
339344 * submitted
340345 */
341- fun sendReport (ankiActivity : AnkiActivity ): Boolean {
346+ override fun sendReport (activity : android.app.Activity ): Boolean {
347+ val ankiActivity = activity as AnkiActivity
342348 val preferences = ankiActivity.sharedPrefs()
343349 val reportMode = preferences.getString(FEEDBACK_REPORT_KEY , " " )
344350 return if (FEEDBACK_REPORT_NEVER == reportMode) {
@@ -359,7 +365,7 @@ object CrashReportService {
359365 val currentTimestamp = TimeManager .time.intTimeMS()
360366 val lastReportTimestamp = getTimestampOfLastReport(activity)
361367 return if (currentTimestamp - lastReportTimestamp > MIN_INTERVAL_MS ) {
362- deleteACRALimiterData (activity)
368+ deleteLimiterData (activity)
363369 sendExceptionReport(
364370 UserSubmittedException (EXCEPTION_MESSAGE ),
365371 " AnkiDroidApp.HelpDialog" ,
@@ -385,45 +391,23 @@ object CrashReportService {
385391}
386392
387393/* *
388- * Runs the provided block, catching [Exception], logging it and reporting it to [CrashReportService]
389- *
390- * **Example**
391- * ```
392- * runCatchingWithReport("callingMethod", onlyIfSilent = true) {
393- * doSomethingRisky()
394- * }
395- * ```
396- *
397- * **Note**: This differs from [runCatching] - `Error` is thrown
398- *
399- * @param origin Data logged to Timber, and provided as the 'origin' field in the error report
400- * @param onlyIfSilent Skip crash report if the crash reporting service is not 'always accept'
401- * @param block Code to execute
402- *
403- * @throws Error If raised, this will be reported and rethrown
404- *
405- * @return A Result containing either the successful result of [block] or the [Exception] thrown
406- */
407- fun <T > runCatchingWithReport (
408- origin : String? ,
409- onlyIfSilent : Boolean = false,
410- block : () -> T ,
411- ): Result <T > =
412- try {
413- Result .success(block())
414- } catch (e: Throwable ) {
415- Timber .w(e, origin)
416- CrashReportService .sendExceptionReport(e, origin, onlyIfSilent = onlyIfSilent)
417- if (e is Error ) throw e
418- Result .failure(e)
419- }
420-
421- /* *
422- * Initializes ACRA crash reporting.
394+ * Initializes ACRA crash reporting and wires it up as the
395+ * global [CrashReportService] reporter.
423396 */
424397context(application: Application )
425398fun initializeAcraCrashReporter () {
426- CrashReportService .initialize(application)
399+ AcraCrashReporter .initialize(application)
400+ CrashReportService .setReporter(AcraCrashReporter )
427401}
428402
429- fun isAcraSenderProcess (): Boolean = CrashReportService .isProperServiceProcess()
403+ fun isAcraSenderProcess (): Boolean = AcraCrashReporter .isProperServiceProcess()
404+
405+ @VisibleForTesting(otherwise = VisibleForTesting .NONE )
406+ val CrashReportService .acraCoreConfigBuilder: CoreConfigurationBuilder
407+ get() = AcraCrashReporter .acraCoreConfigBuilder
408+
409+ @VisibleForTesting(otherwise = VisibleForTesting .NONE )
410+ fun setDebugACRAConfig (sharedPrefs : SharedPreferences ) = AcraCrashReporter .setDebugACRAConfig(sharedPrefs)
411+
412+ @VisibleForTesting(otherwise = VisibleForTesting .NONE )
413+ fun setProductionACRAConfig (sharedPrefs : SharedPreferences ) = AcraCrashReporter .setProductionACRAConfig(sharedPrefs)
0 commit comments