diff --git a/CHANGELOG.md b/CHANGELOG.md index 6daef8c4f4..bbc8da1e88 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,7 @@ ### Features -- Add Replay Custom Masking for iOS ([#4224](https://github.com/getsentry/sentry-react-native/pull/4224)) +- Add Replay Custom Masking for iOS and Android ([#4224](https://github.com/getsentry/sentry-react-native/pull/4224), [#4265](https://github.com/getsentry/sentry-react-native/pull/4265)) ```jsx import * as Sentry from '@sentry/react-native'; diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java index 9366662cc6..6fd2247002 100644 --- a/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java @@ -64,6 +64,8 @@ import io.sentry.protocol.SentryPackage; import io.sentry.protocol.User; import io.sentry.protocol.ViewHierarchy; +import io.sentry.react.replay.RNSentryReplayMask; +import io.sentry.react.replay.RNSentryReplayUnmask; import io.sentry.util.DebugMetaPropertiesApplier; import io.sentry.util.FileUtils; import io.sentry.util.JsonSerializationUtils; @@ -371,6 +373,9 @@ private SentryReplayOptions getReplayOptions(@NotNull ReadableMap rnOptions) { androidReplayOptions.addMaskViewClass("com.horcrux.svg.SvgView"); // react-native-svg } + androidReplayOptions.setMaskViewContainerClass(RNSentryReplayMask.class.getName()); + androidReplayOptions.setUnmaskViewContainerClass(RNSentryReplayUnmask.class.getName()); + return androidReplayOptions; } diff --git a/packages/core/android/src/main/java/io/sentry/react/RNSentryPackage.java b/packages/core/android/src/main/java/io/sentry/react/RNSentryPackage.java index c039a1c002..1af2fe8c89 100644 --- a/packages/core/android/src/main/java/io/sentry/react/RNSentryPackage.java +++ b/packages/core/android/src/main/java/io/sentry/react/RNSentryPackage.java @@ -8,18 +8,35 @@ import com.facebook.react.module.model.ReactModuleInfo; import com.facebook.react.module.model.ReactModuleInfoProvider; import com.facebook.react.uimanager.ViewManager; -import java.util.Arrays; +import io.sentry.react.replay.RNSentryReplayMaskManager; +import io.sentry.react.replay.RNSentryReplayMaskManagerImpl; +import io.sentry.react.replay.RNSentryReplayUnmaskManager; +import io.sentry.react.replay.RNSentryReplayUnmaskManagerImpl; import java.util.HashMap; import java.util.List; import java.util.Map; public class RNSentryPackage extends TurboReactPackage { + private static final boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; + @Nullable @Override public NativeModule getModule(String name, ReactApplicationContext reactContext) { - if (name.equals(RNSentryModuleImpl.NAME)) { + if (RNSentryModuleImpl.NAME.equals(name)) { return new RNSentryModule(reactContext); + } else if (isTurboModule) { + return getFabricComponentNativeModule(name); + } else { + return null; + } + } + + private NativeModule getFabricComponentNativeModule(String name) { + if (RNSentryReplayMaskManagerImpl.REACT_CLASS.equals(name)) { + return new RNSentryReplayMaskManager(); + } else if (RNSentryReplayUnmaskManagerImpl.REACT_CLASS.equals(name)) { + return new RNSentryReplayUnmaskManager(); } else { return null; } @@ -29,7 +46,6 @@ public NativeModule getModule(String name, ReactApplicationContext reactContext) public ReactModuleInfoProvider getReactModuleInfoProvider() { return () -> { final Map moduleInfos = new HashMap<>(); - boolean isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; moduleInfos.put( RNSentryModuleImpl.NAME, new ReactModuleInfo( @@ -41,6 +57,30 @@ public ReactModuleInfoProvider getReactModuleInfoProvider() { false, // isCxxModule isTurboModule // isTurboModule )); + if (isTurboModule) { + moduleInfos.put( + RNSentryReplayMaskManagerImpl.REACT_CLASS, + new ReactModuleInfo( + RNSentryReplayMaskManagerImpl.REACT_CLASS, // name + RNSentryReplayMaskManagerImpl.REACT_CLASS, // className + false, // canOverrideExistingModule + false, // needsEagerInit + false, // hasConstants, required in RN 0.65 + false, // isCxxModule + true // isTurboModule + )); + moduleInfos.put( + RNSentryReplayUnmaskManagerImpl.REACT_CLASS, + new ReactModuleInfo( + RNSentryReplayUnmaskManagerImpl.REACT_CLASS, // name + RNSentryReplayUnmaskManagerImpl.REACT_CLASS, // className + false, // canOverrideExistingModule + false, // needsEagerInit + false, // hasConstants, required in RN 0.65 + false, // isCxxModule + true // isTurboModule + )); + } return moduleInfos; }; } @@ -48,6 +88,9 @@ public ReactModuleInfoProvider getReactModuleInfoProvider() { @NonNull @Override public List createViewManagers(ReactApplicationContext reactContext) { - return Arrays.asList(new RNSentryOnDrawReporterManager(reactContext)); + return List.of( + new RNSentryOnDrawReporterManager(reactContext), + new RNSentryReplayMaskManager(), + new RNSentryReplayUnmaskManager()); } } diff --git a/packages/core/android/src/main/java/io/sentry/react/replay/RNSentryReplayMask.java b/packages/core/android/src/main/java/io/sentry/react/replay/RNSentryReplayMask.java new file mode 100644 index 0000000000..eee70884ef --- /dev/null +++ b/packages/core/android/src/main/java/io/sentry/react/replay/RNSentryReplayMask.java @@ -0,0 +1,10 @@ +package io.sentry.react.replay; + +import android.content.Context; +import com.facebook.react.views.view.ReactViewGroup; + +public class RNSentryReplayMask extends ReactViewGroup { + public RNSentryReplayMask(Context context) { + super(context); + } +} diff --git a/packages/core/android/src/main/java/io/sentry/react/replay/RNSentryReplayMaskManagerImpl.java b/packages/core/android/src/main/java/io/sentry/react/replay/RNSentryReplayMaskManagerImpl.java new file mode 100644 index 0000000000..a5165eb7a7 --- /dev/null +++ b/packages/core/android/src/main/java/io/sentry/react/replay/RNSentryReplayMaskManagerImpl.java @@ -0,0 +1,16 @@ +package io.sentry.react.replay; + +import androidx.annotation.NonNull; +import com.facebook.react.uimanager.ThemedReactContext; + +public final class RNSentryReplayMaskManagerImpl { + + private RNSentryReplayMaskManagerImpl() {} + + public static final String REACT_CLASS = "RNSentryReplayMask"; + + @NonNull + public static RNSentryReplayMask createViewInstance(@NonNull ThemedReactContext context) { + return new RNSentryReplayMask(context); + } +} diff --git a/packages/core/android/src/main/java/io/sentry/react/replay/RNSentryReplayUnmask.java b/packages/core/android/src/main/java/io/sentry/react/replay/RNSentryReplayUnmask.java new file mode 100644 index 0000000000..adc6f2dd25 --- /dev/null +++ b/packages/core/android/src/main/java/io/sentry/react/replay/RNSentryReplayUnmask.java @@ -0,0 +1,10 @@ +package io.sentry.react.replay; + +import android.content.Context; +import com.facebook.react.views.view.ReactViewGroup; + +public class RNSentryReplayUnmask extends ReactViewGroup { + public RNSentryReplayUnmask(Context context) { + super(context); + } +} diff --git a/packages/core/android/src/main/java/io/sentry/react/replay/RNSentryReplayUnmaskManagerImpl.java b/packages/core/android/src/main/java/io/sentry/react/replay/RNSentryReplayUnmaskManagerImpl.java new file mode 100644 index 0000000000..7bee880222 --- /dev/null +++ b/packages/core/android/src/main/java/io/sentry/react/replay/RNSentryReplayUnmaskManagerImpl.java @@ -0,0 +1,16 @@ +package io.sentry.react.replay; + +import androidx.annotation.NonNull; +import com.facebook.react.uimanager.ThemedReactContext; + +public final class RNSentryReplayUnmaskManagerImpl { + + private RNSentryReplayUnmaskManagerImpl() {} + + public static final String REACT_CLASS = "RNSentryReplayUnmask"; + + @NonNull + public RNSentryReplayUnmask createViewInstance(@NonNull ThemedReactContext context) { + return new RNSentryReplayUnmask(context); + } +} diff --git a/packages/core/android/src/newarch/java/io/sentry/react/replay/RNSentryReplayMaskManager.java b/packages/core/android/src/newarch/java/io/sentry/react/replay/RNSentryReplayMaskManager.java new file mode 100644 index 0000000000..aa1ee90825 --- /dev/null +++ b/packages/core/android/src/newarch/java/io/sentry/react/replay/RNSentryReplayMaskManager.java @@ -0,0 +1,33 @@ +package io.sentry.react.replay; + +import androidx.annotation.NonNull; +import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.ViewManagerDelegate; +import com.facebook.react.viewmanagers.RNSentryReplayMaskManagerDelegate; +import com.facebook.react.viewmanagers.RNSentryReplayMaskManagerInterface; + +@ReactModule(name = RNSentryReplayMaskManagerImpl.REACT_CLASS) +public class RNSentryReplayMaskManager extends ViewGroupManager + implements RNSentryReplayMaskManagerInterface { + private final RNSentryReplayMaskManagerDelegate + delegate = new RNSentryReplayMaskManagerDelegate<>(this); + + @Override + public ViewManagerDelegate getDelegate() { + return delegate; + } + + @NonNull + @Override + public String getName() { + return RNSentryReplayMaskManagerImpl.REACT_CLASS; + } + + @NonNull + @Override + public RNSentryReplayMask createViewInstance(@NonNull ThemedReactContext context) { + return new RNSentryReplayMask(context); + } +} diff --git a/packages/core/android/src/newarch/java/io/sentry/react/replay/RNSentryReplayUnmaskManager.java b/packages/core/android/src/newarch/java/io/sentry/react/replay/RNSentryReplayUnmaskManager.java new file mode 100644 index 0000000000..da0648123d --- /dev/null +++ b/packages/core/android/src/newarch/java/io/sentry/react/replay/RNSentryReplayUnmaskManager.java @@ -0,0 +1,34 @@ +package io.sentry.react.replay; + +import androidx.annotation.NonNull; +import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; +import com.facebook.react.uimanager.ViewManagerDelegate; +import com.facebook.react.viewmanagers.RNSentryReplayUnmaskManagerDelegate; +import com.facebook.react.viewmanagers.RNSentryReplayUnmaskManagerInterface; + +@ReactModule(name = RNSentryReplayMaskManagerImpl.REACT_CLASS) +public class RNSentryReplayUnmaskManager extends ViewGroupManager + implements RNSentryReplayUnmaskManagerInterface { + private final RNSentryReplayUnmaskManagerDelegate< + RNSentryReplayUnmask, RNSentryReplayUnmaskManager> + delegate = new RNSentryReplayUnmaskManagerDelegate<>(this); + + @Override + public ViewManagerDelegate getDelegate() { + return delegate; + } + + @NonNull + @Override + public String getName() { + return RNSentryReplayMaskManagerImpl.REACT_CLASS; + } + + @NonNull + @Override + public RNSentryReplayUnmask createViewInstance(@NonNull ThemedReactContext context) { + return new RNSentryReplayUnmask(context); + } +} diff --git a/packages/core/android/src/oldarch/java/io/sentry/react/replay/RNSentryReplayMaskManager.java b/packages/core/android/src/oldarch/java/io/sentry/react/replay/RNSentryReplayMaskManager.java new file mode 100644 index 0000000000..236a7760e4 --- /dev/null +++ b/packages/core/android/src/oldarch/java/io/sentry/react/replay/RNSentryReplayMaskManager.java @@ -0,0 +1,21 @@ +package io.sentry.react.replay; + +import androidx.annotation.NonNull; +import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; + +@ReactModule(name = RNSentryReplayMaskManagerImpl.REACT_CLASS) +public class RNSentryReplayMaskManager extends ViewGroupManager { + @NonNull + @Override + public String getName() { + return RNSentryReplayMaskManagerImpl.REACT_CLASS; + } + + @NonNull + @Override + public RNSentryReplayMask createViewInstance(@NonNull ThemedReactContext context) { + return new RNSentryReplayMask(context); + } +} diff --git a/packages/core/android/src/oldarch/java/io/sentry/react/replay/RNSentryReplayUnmaskManager.java b/packages/core/android/src/oldarch/java/io/sentry/react/replay/RNSentryReplayUnmaskManager.java new file mode 100644 index 0000000000..86732eca38 --- /dev/null +++ b/packages/core/android/src/oldarch/java/io/sentry/react/replay/RNSentryReplayUnmaskManager.java @@ -0,0 +1,21 @@ +package io.sentry.react.replay; + +import androidx.annotation.NonNull; +import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.uimanager.ThemedReactContext; +import com.facebook.react.uimanager.ViewGroupManager; + +@ReactModule(name = RNSentryReplayUnmaskManagerImpl.REACT_CLASS) +public class RNSentryReplayUnmaskManager extends ViewGroupManager { + @NonNull + @Override + public String getName() { + return RNSentryReplayUnmaskManagerImpl.REACT_CLASS; + } + + @NonNull + @Override + public RNSentryReplayUnmask createViewInstance(@NonNull ThemedReactContext context) { + return new RNSentryReplayUnmask(context); + } +}