diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ae55713ab..6af90577b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Features +- Add Screenshot and ViewHierarchy to integrations list ([#2698](https://github.com/getsentry/sentry-java/pull/2698)) - New ANR detection based on [ApplicationExitInfo API](https://developer.android.com/reference/android/app/ApplicationExitInfo) ([#2697](https://github.com/getsentry/sentry-java/pull/2697)) - This implementation completely replaces the old one (based on a watchdog) on devices running Android 11 and above: - New implementation provides more precise ANR events/ANR rate detection as well as system thread dump information. The new implementation reports ANRs exactly as Google Play Console, without producing false positives or missing important background ANR events. diff --git a/sentry-android-core/api/sentry-android-core.api b/sentry-android-core/api/sentry-android-core.api index fcc1fb914b..ae31c5cb8a 100644 --- a/sentry-android-core/api/sentry-android-core.api +++ b/sentry-android-core/api/sentry-android-core.api @@ -186,7 +186,7 @@ public final class io/sentry/android/core/PhoneStateBreadcrumbsIntegration : io/ public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } -public final class io/sentry/android/core/ScreenshotEventProcessor : io/sentry/EventProcessor { +public final class io/sentry/android/core/ScreenshotEventProcessor : io/sentry/EventProcessor, io/sentry/IntegrationName { public fun (Lio/sentry/android/core/SentryAndroidOptions;Lio/sentry/android/core/BuildInfoProvider;)V public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; } @@ -311,7 +311,7 @@ public final class io/sentry/android/core/UserInteractionIntegration : android/a public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V } -public final class io/sentry/android/core/ViewHierarchyEventProcessor : io/sentry/EventProcessor { +public final class io/sentry/android/core/ViewHierarchyEventProcessor : io/sentry/EventProcessor, io/sentry/IntegrationName { public fun (Lio/sentry/android/core/SentryAndroidOptions;)V public fun process (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Lio/sentry/SentryEvent; public static fun snapshotViewHierarchy (Landroid/app/Activity;Lio/sentry/ILogger;)Lio/sentry/protocol/ViewHierarchy; diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java index 550634d309..45245bb897 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ScreenshotEventProcessor.java @@ -7,6 +7,7 @@ import io.sentry.Attachment; import io.sentry.EventProcessor; import io.sentry.Hint; +import io.sentry.IntegrationName; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.util.HintUtils; @@ -20,7 +21,7 @@ * captured. */ @ApiStatus.Internal -public final class ScreenshotEventProcessor implements EventProcessor { +public final class ScreenshotEventProcessor implements EventProcessor, IntegrationName { private final @NotNull SentryAndroidOptions options; private final @NotNull BuildInfoProvider buildInfoProvider; @@ -31,6 +32,9 @@ public ScreenshotEventProcessor( this.options = Objects.requireNonNull(options, "SentryAndroidOptions is required"); this.buildInfoProvider = Objects.requireNonNull(buildInfoProvider, "BuildInfoProvider is required"); + if (options.isAttachScreenshot()) { + addIntegrationToSdkVersion(); + } } @Override diff --git a/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java b/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java index 675715a383..ba5c8bbfdb 100644 --- a/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java +++ b/sentry-android-core/src/main/java/io/sentry/android/core/ViewHierarchyEventProcessor.java @@ -9,6 +9,7 @@ import io.sentry.Hint; import io.sentry.ILogger; import io.sentry.ISerializer; +import io.sentry.IntegrationName; import io.sentry.SentryEvent; import io.sentry.SentryLevel; import io.sentry.android.core.internal.gestures.ViewUtils; @@ -25,12 +26,15 @@ /** ViewHierarchyEventProcessor responsible for taking a snapshot of the current view hierarchy. */ @ApiStatus.Internal -public final class ViewHierarchyEventProcessor implements EventProcessor { +public final class ViewHierarchyEventProcessor implements EventProcessor, IntegrationName { private final @NotNull SentryAndroidOptions options; public ViewHierarchyEventProcessor(final @NotNull SentryAndroidOptions options) { this.options = Objects.requireNonNull(options, "SentryAndroidOptions is required"); + if (options.isAttachViewHierarchy()) { + addIntegrationToSdkVersion(); + } } @Override diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt index 2d208db720..5fa20184b5 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ScreenshotEventProcessorTest.kt @@ -8,6 +8,7 @@ import io.sentry.Attachment import io.sentry.Hint import io.sentry.MainEventProcessor import io.sentry.SentryEvent +import io.sentry.SentryIntegrationPackageStorage import io.sentry.TypeCheckHint.ANDROID_ACTIVITY import org.junit.runner.RunWith import org.mockito.kotlin.mock @@ -15,6 +16,7 @@ import org.mockito.kotlin.whenever import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals +import kotlin.test.assertFalse import kotlin.test.assertNull import kotlin.test.assertSame import kotlin.test.assertTrue @@ -153,5 +155,25 @@ class ScreenshotEventProcessorTest { assertNull(hint.screenshot) } + @Test + fun `when enabled, the feature is added to the integration list`() { + SentryIntegrationPackageStorage.getInstance().clearStorage() + val hint = Hint() + val sut = fixture.getSut(true) + val event = fixture.mainProcessor.process(getEvent(), hint) + sut.process(event, hint) + assertTrue(fixture.options.sdkVersion!!.integrationSet.contains("Screenshot")) + } + + @Test + fun `when not enabled, the feature is not added to the integration list`() { + SentryIntegrationPackageStorage.getInstance().clearStorage() + val hint = Hint() + val sut = fixture.getSut(false) + val event = fixture.mainProcessor.process(getEvent(), hint) + sut.process(event, hint) + assertFalse(fixture.options.sdkVersion!!.integrationSet.contains("Screenshot")) + } + private fun getEvent(): SentryEvent = SentryEvent(Throwable("Throwable")) } diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt index f6a4a99506..a9327d1be9 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/SentryAndroidOptionsTest.kt @@ -104,6 +104,13 @@ class SentryAndroidOptionsTest { assertFalse(sentryOptions.isEnableUserInteractionTracing) } + @Test + fun `attach view hierarchy is disabled by default for Android`() { + val sentryOptions = SentryAndroidOptions() + + assertFalse(sentryOptions.isAttachViewHierarchy) + } + private class CustomDebugImagesLoader : IDebugImagesLoader { override fun loadDebugImages(): List? = null override fun clearDebugImages() {} diff --git a/sentry-android-core/src/test/java/io/sentry/android/core/ViewHierarchyEventProcessorTest.kt b/sentry-android-core/src/test/java/io/sentry/android/core/ViewHierarchyEventProcessorTest.kt index a74e83858a..a377032c9a 100644 --- a/sentry-android-core/src/test/java/io/sentry/android/core/ViewHierarchyEventProcessorTest.kt +++ b/sentry-android-core/src/test/java/io/sentry/android/core/ViewHierarchyEventProcessorTest.kt @@ -9,6 +9,7 @@ import io.sentry.Hint import io.sentry.JsonSerializable import io.sentry.JsonSerializer import io.sentry.SentryEvent +import io.sentry.SentryIntegrationPackageStorage import io.sentry.TypeCheckHint import io.sentry.protocol.SentryException import org.junit.runner.RunWith @@ -23,6 +24,7 @@ import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNotNull import kotlin.test.assertNull +import kotlin.test.assertTrue @RunWith(AndroidJUnit4::class) class ViewHierarchyEventProcessorTest { @@ -263,6 +265,30 @@ class ViewHierarchyEventProcessorTest { } } + @Test + fun `when enabled, the feature is added to the integration list`() { + SentryIntegrationPackageStorage.getInstance().clearStorage() + val (event, hint) = fixture.process( + true, + SentryEvent().apply { + exceptions = listOf(SentryException()) + } + ) + assertTrue(fixture.options.sdkVersion!!.integrationSet.contains("ViewHierarchy")) + } + + @Test + fun `when not enabled, the feature is not added to the integration list`() { + SentryIntegrationPackageStorage.getInstance().clearStorage() + val (event, hint) = fixture.process( + false, + SentryEvent().apply { + exceptions = listOf(SentryException()) + } + ) + assertFalse(fixture.options.sdkVersion!!.integrationSet.contains("ViewHierarchy")) + } + private fun mockedView( x: Float, y: Float, diff --git a/sentry/src/main/java/io/sentry/IntegrationName.java b/sentry/src/main/java/io/sentry/IntegrationName.java index 585eea34d8..ca3e98cd2c 100644 --- a/sentry/src/main/java/io/sentry/IntegrationName.java +++ b/sentry/src/main/java/io/sentry/IntegrationName.java @@ -6,7 +6,8 @@ default String getIntegrationName() { .getSimpleName() .replace("Sentry", "") .replace("Integration", "") - .replace("Interceptor", ""); + .replace("Interceptor", "") + .replace("EventProcessor", ""); } default void addIntegrationToSdkVersion() {