From 4d336de0702183a2b726a484a7d38d43d0fb4147 Mon Sep 17 00:00:00 2001 From: androidacy-user Date: Tue, 6 Jun 2023 21:33:48 -0400 Subject: [PATCH] move sentry integration to kotlin Signed-off-by: androidacy-user --- .../mmm/utils/sentry/SentryBreadcrumb.kt | 41 ++-- .../fox2code/mmm/utils/sentry/SentryMain.kt | 214 +++++++++--------- 2 files changed, 130 insertions(+), 125 deletions(-) diff --git a/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryBreadcrumb.kt b/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryBreadcrumb.kt index 747d077..9f5a22b 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryBreadcrumb.kt +++ b/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryBreadcrumb.kt @@ -1,32 +1,29 @@ -package com.fox2code.mmm.utils.sentry; +package com.fox2code.mmm.utils.sentry -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; +import io.sentry.Breadcrumb +import io.sentry.SentryLevel +import java.util.Objects -import java.util.Objects; +class SentryBreadcrumb { + @JvmField + val breadcrumb: Breadcrumb = Breadcrumb() -import io.sentry.Breadcrumb; -import io.sentry.SentryLevel; - -public class SentryBreadcrumb { - public final Breadcrumb breadcrumb; - - public SentryBreadcrumb() { - breadcrumb = new Breadcrumb(); - breadcrumb.setLevel(SentryLevel.INFO); + init { + breadcrumb.level = SentryLevel.INFO } - public void setType(@Nullable String type) { - breadcrumb.setType(type); + fun setType(type: String?) { + breadcrumb.type = type } - public void setData(@NotNull String key, @Nullable Object value) { - if (value == null) value = "null"; - Objects.requireNonNull(key); - breadcrumb.setData(key, value); + fun setData(key: String, value: Any?) { + @Suppress("NAME_SHADOWING") var value = value + if (value == null) value = "null" + Objects.requireNonNull(key) + breadcrumb.setData(key, value) } - public void setCategory(@Nullable String category) { - breadcrumb.setCategory(category); + fun setCategory(category: String?) { + breadcrumb.category = category } -} +} \ No newline at end of file diff --git a/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.kt b/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.kt index 1ddc697..02bdf59 100644 --- a/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.kt +++ b/app/src/main/java/com/fox2code/mmm/utils/sentry/SentryMain.kt @@ -1,143 +1,151 @@ -package com.fox2code.mmm.utils.sentry; +package com.fox2code.mmm.utils.sentry -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.net.Uri; +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.content.SharedPreferences +import android.net.Uri +import android.os.Process +import com.fox2code.mmm.CrashHandler +import com.fox2code.mmm.MainApplication +import com.fox2code.mmm.androidacy.AndroidacyUtil.Companion.hideToken +import com.fox2code.mmm.androidacy.AndroidacyUtil.Companion.isAndroidacyLink +import io.sentry.Breadcrumb +import io.sentry.Hint +import io.sentry.Sentry +import io.sentry.SentryEvent +import io.sentry.SentryOptions.BeforeBreadcrumbCallback +import io.sentry.SentryOptions.BeforeSendCallback +import io.sentry.android.core.SentryAndroid +import io.sentry.android.core.SentryAndroidOptions +import io.sentry.android.fragment.FragmentLifecycleIntegration +import io.sentry.android.timber.SentryTimberIntegration +import org.matomo.sdk.extra.TrackHelper +import timber.log.Timber -import com.fox2code.mmm.CrashHandler; -import com.fox2code.mmm.MainApplication; -import com.fox2code.mmm.androidacy.AndroidacyUtil; - -import org.matomo.sdk.extra.TrackHelper; - -import java.util.Objects; - -import io.sentry.Sentry; -import io.sentry.android.core.SentryAndroid; -import io.sentry.android.fragment.FragmentLifecycleIntegration; -import io.sentry.android.timber.SentryTimberIntegration; -import timber.log.Timber; - -public class SentryMain { - public static final boolean IS_SENTRY_INSTALLED = true; - public static boolean isCrashing = false; - private static boolean sentryEnabled = false; +object SentryMain { + const val IS_SENTRY_INSTALLED = true + private var isCrashing = false + private var isSentryEnabled = false /** * Initialize Sentry * Sentry is used for crash reporting and performance monitoring. */ - @SuppressLint({"RestrictedApi", "UnspecifiedImmutableFlag"}) - public static void initialize(final MainApplication mainApplication) { - Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { - isCrashing = true; - TrackHelper.track().exception(throwable).with(MainApplication.getINSTANCE().getTracker()); - SharedPreferences.Editor editor = MainApplication.getINSTANCE().getSharedPreferences("sentry", Context.MODE_PRIVATE).edit(); - editor.putString("lastExitReason", "crash"); - editor.putLong("lastExitTime", System.currentTimeMillis()); - editor.putString("lastExitReason", "crash"); - editor.putString("lastExitId", String.valueOf(Sentry.getLastEventId())); - editor.apply(); - Timber.e("Uncaught exception with sentry ID %s and stacktrace %s", Sentry.getLastEventId(), throwable.getStackTrace()); + @JvmStatic + @SuppressLint("RestrictedApi", "UnspecifiedImmutableFlag") + fun initialize(mainApplication: MainApplication) { + Thread.setDefaultUncaughtExceptionHandler { _: Thread?, throwable: Throwable -> + isCrashing = true + TrackHelper.track().exception(throwable).with(MainApplication.getINSTANCE().tracker) + val editor = + MainApplication.getINSTANCE().getSharedPreferences("sentry", Context.MODE_PRIVATE) + .edit() + editor.putString("lastExitReason", "crash") + editor.putLong("lastExitTime", System.currentTimeMillis()) + editor.putString("lastExitReason", "crash") + editor.putString("lastExitId", Sentry.getLastEventId().toString()) + editor.apply() + Timber.e( + "Uncaught exception with sentry ID %s and stacktrace %s", + Sentry.getLastEventId(), + throwable.stackTrace + ) // open crash handler and exit - Intent intent = new Intent(mainApplication, CrashHandler.class); + val intent = Intent(mainApplication, CrashHandler::class.java) // pass the entire exception to the crash handler - intent.putExtra("exception", throwable); + intent.putExtra("exception", throwable) // add stacktrace as string - intent.putExtra("stacktrace", throwable.getStackTrace()); + intent.putExtra("stacktrace", throwable.stackTrace) // put lastEventId in intent (get from preferences) - intent.putExtra("lastEventId", String.valueOf(Sentry.getLastEventId())); + intent.putExtra("lastEventId", Sentry.getLastEventId().toString()) // serialize Sentry.captureException and pass it to the crash handler - intent.putExtra("sentryException", throwable); + intent.putExtra("sentryException", throwable) // pass crashReportingEnabled to crash handler - intent.putExtra("crashReportingEnabled", isSentryEnabled()); + intent.putExtra("crashReportingEnabled", isSentryEnabled) // add isCrashing to intent - intent.putExtra("isCrashing", isCrashing); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); - Timber.e("Starting crash handler"); - mainApplication.startActivity(intent); - Timber.e("Exiting"); - android.os.Process.killProcess(android.os.Process.myPid()); - }); + intent.putExtra("isCrashing", isCrashing) + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + Timber.e("Starting crash handler") + mainApplication.startActivity(intent) + Timber.e("Exiting") + Process.killProcess(Process.myPid()) + } // If first_launch pref is not false, refuse to initialize Sentry - SharedPreferences sharedPreferences = MainApplication.getSharedPreferences("mmm"); - if (!Objects.equals(sharedPreferences.getString("last_shown_setup", null), "v2")) { - return; + val sharedPreferences = MainApplication.getSharedPreferences("mmm") + if (sharedPreferences.getString("last_shown_setup", null) != "v2") { + return } - sentryEnabled = sharedPreferences.getBoolean("pref_crash_reporting_enabled", false); + isSentryEnabled = sharedPreferences.getBoolean("pref_crash_reporting_enabled", false) // set sentryEnabled on preference change of pref_crash_reporting_enabled - sharedPreferences.registerOnSharedPreferenceChangeListener((sharedPreferences1, s) -> { - if (s.equals("pref_crash_reporting_enabled")) { - sentryEnabled = sharedPreferences1.getBoolean(s, false); + sharedPreferences.registerOnSharedPreferenceChangeListener { sharedPreferences1: SharedPreferences, s: String -> + if (s == "pref_crash_reporting_enabled") { + isSentryEnabled = sharedPreferences1.getBoolean(s, false) } - }); - SentryAndroid.init(mainApplication, options -> { + } + SentryAndroid.init(mainApplication) { options: SentryAndroidOptions -> // If crash reporting is disabled, stop here. if (!MainApplication.isCrashReportingEnabled()) { - sentryEnabled = false; // Set sentry state to disabled - options.setDsn(""); + isSentryEnabled = false // Set sentry state to disabled + options.dsn = "" } else { // get pref_crash_reporting_pii pref - boolean crashReportingPii = sharedPreferences.getBoolean("crashReportingPii", false); - sentryEnabled = true; // Set sentry state to enabled - options.addIntegration(new FragmentLifecycleIntegration(mainApplication, true, true)); + val crashReportingPii = sharedPreferences.getBoolean("crashReportingPii", false) + isSentryEnabled = true // Set sentry state to enabled + options.addIntegration(FragmentLifecycleIntegration(mainApplication, + enableFragmentLifecycleBreadcrumbs = true, + enableAutoFragmentLifecycleTracing = true + )) // Enable automatic activity lifecycle breadcrumbs - options.setEnableActivityLifecycleBreadcrumbs(true); + options.isEnableActivityLifecycleBreadcrumbs = true // Enable automatic fragment lifecycle breadcrumbs - options.addIntegration(new SentryTimberIntegration()); - options.setCollectAdditionalContext(true); - options.setAttachThreads(true); - options.setAttachStacktrace(true); - options.setEnableNdk(true); - options.addInAppInclude("com.fox2code.mmm"); - options.addInAppInclude("com.fox2code.mmm.debug"); - options.addInAppInclude("com.fox2code.mmm.fdroid"); - options.addInAppExclude("com.fox2code.mmm.utils.sentry.SentryMain"); + options.addIntegration(SentryTimberIntegration()) + options.isCollectAdditionalContext = true + options.isAttachThreads = true + options.isAttachStacktrace = true + options.isEnableNdk = true + options.addInAppInclude("com.fox2code.mmm") + options.addInAppInclude("com.fox2code.mmm.debug") + options.addInAppInclude("com.fox2code.mmm.fdroid") + options.addInAppExclude("com.fox2code.mmm.utils.sentry.SentryMain") // Respect user preference for sending PII. default is true on non fdroid builds, false on fdroid builds - options.setSendDefaultPii(crashReportingPii); - options.enableAllAutoBreadcrumbs(true); + options.isSendDefaultPii = crashReportingPii + options.enableAllAutoBreadcrumbs(true) // in-app screenshots are only sent if the app crashes, and it only shows the last activity. so no, we won't see your, ahem, "private" stuff - options.setAttachScreenshot(true); + options.isAttachScreenshot = true // It just tell if sentry should ping the sentry dsn to tell the app is running. Useful for performance and profiling. - options.setEnableAutoSessionTracking(true); + options.isEnableAutoSessionTracking = true // disable crash tracking - we handle that ourselves - options.setEnableUncaughtExceptionHandler(false); + options.isEnableUncaughtExceptionHandler = false // Add a callback that will be used before the event is sent to Sentry. // With this callback, you can modify the event or, when returning null, also discard the event. - options.setBeforeSend((event, hint) -> { + options.beforeSend = BeforeSendCallback { event: SentryEvent?, _: Hint? -> // in the rare event that crash reporting has been disabled since we started the app, we don't want to send the crash report - if (!sentryEnabled) { - return null; - } - if (isCrashing) { - return null; + if (!isSentryEnabled || isCrashing) { + return@BeforeSendCallback null } - return event; - }); + event + } // Filter breadcrumb content from crash report. - options.setBeforeBreadcrumb((breadcrumb, hint) -> { - String url = (String) breadcrumb.getData("url"); - if (url == null || url.isEmpty()) return breadcrumb; - if ("cloudflare-dns.com".equals(Uri.parse(url).getHost())) return null; - if (AndroidacyUtil.Companion.isAndroidacyLink(url)) { - breadcrumb.setData("url", AndroidacyUtil.hideToken(url)); + options.beforeBreadcrumb = + BeforeBreadcrumbCallback { breadcrumb: Breadcrumb, _: Hint? -> + val url = breadcrumb.getData("url") as String? + if (url.isNullOrEmpty()) return@BeforeBreadcrumbCallback null + if ("cloudflare-dns.com" == Uri.parse(url).host) { + return@BeforeBreadcrumbCallback null + } + if (isAndroidacyLink(url)) { + breadcrumb.setData("url", hideToken(url)) + } + breadcrumb } - return breadcrumb; - }); } - }); + } } - public static void addSentryBreadcrumb(SentryBreadcrumb sentryBreadcrumb) { + fun addSentryBreadcrumb(sentryBreadcrumb: SentryBreadcrumb) { if (MainApplication.isCrashReportingEnabled()) { - Sentry.addBreadcrumb(sentryBreadcrumb.breadcrumb); + Sentry.addBreadcrumb(sentryBreadcrumb.breadcrumb) } } - - @SuppressWarnings("unused") - public static boolean isSentryEnabled() { - return sentryEnabled; - } -} +} \ No newline at end of file