diff --git a/CHANGELOG.md b/CHANGELOG.md index 5beab9e551..184585e6a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ ## Unreleased +### Changes + +- Fallback to Current Activity Holder when React Context Activity is not present ([#4779](https://github.com/getsentry/sentry-react-native/pull/4779)) + ### Dependencies - Bump Cocoa SDK from v8.49.1 to v8.49.2 ([#4792](https://github.com/getsentry/sentry-react-native/pull/4792)) diff --git a/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/utils/RNSentryActivityUtilsTest.kt b/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/utils/RNSentryActivityUtilsTest.kt new file mode 100644 index 0000000000..3887b9991d --- /dev/null +++ b/packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/utils/RNSentryActivityUtilsTest.kt @@ -0,0 +1,52 @@ +package io.sentry.react.utils + +import android.app.Activity +import com.facebook.react.bridge.ReactApplicationContext +import io.sentry.android.core.AndroidLogger +import io.sentry.android.core.CurrentActivityHolder +import org.junit.After +import org.junit.Assert.assertNull +import org.junit.Assert.assertSame +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mockito.mock +import org.mockito.kotlin.whenever + +@RunWith(JUnit4::class) +class RNSentryActivityUtilsTest { + private val mockedLogger = mock(AndroidLogger::class.java) + + @After + fun clearActivityHolder() { + CurrentActivityHolder.getInstance().clearActivity() + } + + @Test + fun `returns react context activity`() { + val mockedCurrentActivity = mock(Activity::class.java) + val mockedReactContext = mock(ReactApplicationContext::class.java) + whenever(mockedReactContext.currentActivity).thenReturn(mockedCurrentActivity) + + assertSame(RNSentryActivityUtils.getCurrentActivity(mockedReactContext, mockedLogger), mockedCurrentActivity) + } + + @Test + fun `returns current activity holder activity`() { + val mockedCurrentActivity = mock(Activity::class.java) + + val mockedReactContext = mock(ReactApplicationContext::class.java) + whenever(mockedReactContext.currentActivity).thenReturn(null) + + CurrentActivityHolder.getInstance().setActivity(mockedCurrentActivity) + assertSame(RNSentryActivityUtils.getCurrentActivity(mockedReactContext, mockedLogger), mockedCurrentActivity) + } + + @Test + fun `returns null when no activity exists`() { + val mockedReactContext = mock(ReactApplicationContext::class.java) + whenever(mockedReactContext.currentActivity).thenReturn(null) + + assertNull(RNSentryActivityUtils.getCurrentActivity(mockedReactContext, mockedLogger)) + } +} diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryOnDrawReporterManager.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryOnDrawReporterManager.java index a622444658..ce7497db7c 100644 --- a/packages/core/android/src/main/java/io/sentry/react/RNSentryOnDrawReporterManager.java +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryOnDrawReporterManager.java @@ -14,6 +14,7 @@ import io.sentry.android.core.BuildInfoProvider; import io.sentry.android.core.SentryAndroidDateProvider; import io.sentry.android.core.internal.util.FirstDrawDoneListener; +import io.sentry.react.utils.RNSentryActivityUtils; import java.util.Objects; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -151,11 +152,12 @@ private void processPropsChanged() { return; } - @Nullable Activity activity = reactContext.getCurrentActivity(); + final @Nullable Activity activity = + RNSentryActivityUtils.getCurrentActivity(reactContext, logger); if (activity == null) { logger.log( SentryLevel.ERROR, - "[TimeToDisplay] Won't emit next frame drawn event, reactContext is null."); + "[TimeToDisplay] Won't emit next frame drawn event, activity is null."); return; } diff --git a/packages/core/android/src/main/java/io/sentry/react/utils/RNSentryActivityUtils.java b/packages/core/android/src/main/java/io/sentry/react/utils/RNSentryActivityUtils.java new file mode 100644 index 0000000000..c63ab29553 --- /dev/null +++ b/packages/core/android/src/main/java/io/sentry/react/utils/RNSentryActivityUtils.java @@ -0,0 +1,31 @@ +package io.sentry.react.utils; + +import android.app.Activity; +import com.facebook.react.bridge.ReactApplicationContext; +import io.sentry.ILogger; +import io.sentry.SentryLevel; +import io.sentry.android.core.CurrentActivityHolder; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** Utility class for React Native Activity related functionality. */ +public final class RNSentryActivityUtils { + + private RNSentryActivityUtils() { + // Prevent instantiation + } + + public static @Nullable Activity getCurrentActivity( + final @NotNull ReactApplicationContext reactContext, final @NotNull ILogger logger) { + final Activity activity = reactContext.getCurrentActivity(); + if (activity != null) { + return activity; + } + + logger.log( + SentryLevel.DEBUG, + "[RNSentryActivityUtils] Given ReactApplicationContext has no activity attached, using" + + " CurrentActivityHolder as a fallback."); + return CurrentActivityHolder.getInstance().getActivity(); + } +}