Skip to content

Commit 2225fc5

Browse files
romtsnantonis
andauthored
feat(replay): Add screenshotStrategy option for Android (#5301)
* feat(replay): Add screenshotStrategy option for Android * Remove type export * Changelog * Changelog * Update packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java Co-authored-by: Antonis Lilis <[email protected]> --------- Co-authored-by: Antonis Lilis <[email protected]>
1 parent 4167e15 commit 2225fc5

File tree

4 files changed

+60
-0
lines changed

4 files changed

+60
-0
lines changed

CHANGELOG.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,22 @@
1212

1313
- Adds GraphQL integration ([#5299](https://github.com/getsentry/sentry-react-native/pull/5299))
1414
- Adds Supabase integration ([#5296](https://github.com/getsentry/sentry-react-native/pull/5296))
15+
- Add new _experimental_ Canvas Capture Strategy for Session Replay ([#5301](https://github.com/getsentry/sentry-react-native/pull/5301))
16+
- A new screenshot capture strategy that uses Android's Canvas API for more accurate and reliable text and image masking
17+
- Any `.drawText()` or `.drawBitmap()` calls are replaced by rectangles, ensuring no text or images are present in the resulting output
18+
- Note: If this strategy is used, all text and images will be masked, regardless of any masking configuration
19+
- To enable this feature, set the `screenshotStrategy` to `canvas`:
20+
```js
21+
import * as Sentry from '@sentry/react-native';
22+
23+
Sentry.init({
24+
integrations: [
25+
Sentry.mobileReplayIntegration({
26+
screenshotStrategy: 'canvas',
27+
}),
28+
],
29+
});
30+
```
1531

1632
### Dependencies
1733

packages/core/android/src/main/java/io/sentry/react/RNSentryModuleImpl.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import io.sentry.ISerializer;
3838
import io.sentry.Integration;
3939
import io.sentry.ScopesAdapter;
40+
import io.sentry.ScreenshotStrategyType;
4041
import io.sentry.Sentry;
4142
import io.sentry.SentryDate;
4243
import io.sentry.SentryDateProvider;
@@ -415,12 +416,32 @@ private SentryReplayOptions getReplayOptions(@NotNull ReadableMap rnOptions) {
415416
androidReplayOptions.addMaskViewClass("com.horcrux.svg.SvgView"); // react-native-svg
416417
}
417418

419+
if (rnMobileReplayOptions.hasKey("screenshotStrategy")) {
420+
final String screenshotStrategyString = rnMobileReplayOptions.getString("screenshotStrategy");
421+
final ScreenshotStrategyType screenshotStrategy =
422+
parseScreenshotStrategy(screenshotStrategyString);
423+
androidReplayOptions.setScreenshotStrategy(screenshotStrategy);
424+
}
425+
418426
androidReplayOptions.setMaskViewContainerClass(RNSentryReplayMask.class.getName());
419427
androidReplayOptions.setUnmaskViewContainerClass(RNSentryReplayUnmask.class.getName());
420428

421429
return androidReplayOptions;
422430
}
423431

432+
private ScreenshotStrategyType parseScreenshotStrategy(@Nullable String strategyString) {
433+
if (strategyString == null) {
434+
return ScreenshotStrategyType.PIXEL_COPY;
435+
}
436+
437+
switch (strategyString.toLowerCase(Locale.ROOT)) {
438+
case "canvas":
439+
return ScreenshotStrategyType.CANVAS;
440+
default:
441+
return ScreenshotStrategyType.PIXEL_COPY;
442+
}
443+
}
444+
424445
private SentryReplayQuality parseReplayQuality(@Nullable String qualityString) {
425446
if (qualityString == null) {
426447
return SentryReplayQuality.MEDIUM;

packages/core/src/js/replay/mobilereplay.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ import { enrichXhrBreadcrumbsForMobileReplay } from './xhrUtils';
88

99
export const MOBILE_REPLAY_INTEGRATION_NAME = 'MobileReplay';
1010

11+
/**
12+
* Screenshot strategy type for Android Session Replay.
13+
*
14+
* - `'canvas'`: Canvas-based screenshot strategy. This strategy does **not** support any masking options, it always masks text and images. Use this if your application has strict PII requirements.
15+
* - `'pixelCopy'`: Pixel copy screenshot strategy (default). Supports all masking options.
16+
*/
17+
export type ScreenshotStrategy = 'canvas' | 'pixelCopy';
18+
1119
export interface MobileReplayOptions {
1220
/**
1321
* Mask all text in recordings
@@ -69,6 +77,19 @@ export interface MobileReplayOptions {
6977
* @default false
7078
*/
7179
enableFastViewRendering?: boolean;
80+
81+
/**
82+
* Sets the screenshot strategy used by the Session Replay integration on Android.
83+
*
84+
* If your application has strict PII requirements we recommend using `'canvas'`.
85+
* This strategy does **not** support any masking options, it always masks text and images.
86+
*
87+
* - Experiment: In case you are noticing issues with the canvas screenshot strategy, please report the issue on [GitHub](https://github.com/getsentry/sentry-java).
88+
*
89+
* @default 'pixelCopy'
90+
* @platform android
91+
*/
92+
screenshotStrategy?: ScreenshotStrategy;
7293
}
7394

7495
const defaultOptions: Required<MobileReplayOptions> = {
@@ -78,6 +99,7 @@ const defaultOptions: Required<MobileReplayOptions> = {
7899
enableExperimentalViewRenderer: false,
79100
enableViewRendererV2: true,
80101
enableFastViewRendering: false,
102+
screenshotStrategy: 'pixelCopy',
81103
};
82104

83105
function mergeOptions(initOptions: Partial<MobileReplayOptions>): Required<MobileReplayOptions> {

samples/react-native/src/App.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ Sentry.init({
9494
maskAllVectors: true,
9595
maskAllText: true,
9696
enableViewRendererV2: true,
97+
screenshotStrategy: 'canvas', // if you have strict PII requirements
9798
}),
9899
Sentry.appStartIntegration({
99100
standalone: false,

0 commit comments

Comments
 (0)