From bab6449f5c75af532fc7c7151944503497f13193 Mon Sep 17 00:00:00 2001 From: Krystof Woldrich Date: Fri, 25 Apr 2025 18:58:52 +0200 Subject: [PATCH 1/2] chore(ttd): Fallback to activity holder if react context activity is not available --- CHANGELOG.md | 1 + .../react/utils/RNSentryActivityUtilsTest.kt | 52 +++++++++++++++++++ .../react/RNSentryOnDrawReporterManager.java | 6 ++- .../react/utils/RNSentryActivityUtils.java | 31 +++++++++++ 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 packages/core/RNSentryAndroidTester/app/src/test/java/io/sentry/react/utils/RNSentryActivityUtilsTest.kt create mode 100644 packages/core/android/src/main/java/io/sentry/react/utils/RNSentryActivityUtils.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 819f1e1a2e..a89c30abb2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ - Export `extraErrorDataIntegration` from `@sentry/core` ([#4762](https://github.com/getsentry/sentry-react-native/pull/4762)) - Remove `@sentry-internal/replay` when `includeWebReplay: false` ([#4774](https://github.com/getsentry/sentry-react-native/pull/4774)) +- Fallback to Current Activity Holder when React Context Activity is not present ([#4779](https://github.com/getsentry/sentry-react-native/pull/4779)) ### Dependencies 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(); + } +} From 302bbbb3e923330ada76eb93ef05d2685c0432f3 Mon Sep 17 00:00:00 2001 From: Krystof Woldrich <31292499+krystofwoldrich@users.noreply.github.com> Date: Tue, 29 Apr 2025 15:42:32 +0200 Subject: [PATCH 2/2] Update CHANGELOG.md --- CHANGELOG.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b3b0c0692..202bf215a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ > make sure you follow our [migration guide](https://docs.sentry.io/platforms/react-native/migration/) first. +## Unreleased + +### Changes + +- Fallback to Current Activity Holder when React Context Activity is not present ([#4779](https://github.com/getsentry/sentry-react-native/pull/4779)) + ## 6.12.0 ### Features @@ -17,7 +23,6 @@ - Export `extraErrorDataIntegration` from `@sentry/core` ([#4762](https://github.com/getsentry/sentry-react-native/pull/4762)) - Remove `@sentry-internal/replay` when `includeWebReplay: false` ([#4774](https://github.com/getsentry/sentry-react-native/pull/4774)) -- Fallback to Current Activity Holder when React Context Activity is not present ([#4779](https://github.com/getsentry/sentry-react-native/pull/4779)) ### Dependencies