Skip to content

Commit 495e7bc

Browse files
romtsnclaude
andcommitted
Add screenshot masking documentation for Android
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 4aa6c6e commit 495e7bc

File tree

2 files changed

+175
-0
lines changed
  • docs/platforms/android/enriching-events/screenshots
  • platform-includes/enriching-events/attach-screenshots

2 files changed

+175
-0
lines changed

docs/platforms/android/enriching-events/screenshots/index.mdx

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,175 @@ Because screenshots may contain <PlatformLink to="/data-management/sensitive-dat
1616

1717
<PlatformContent includePath="enriching-events/attach-screenshots" />
1818

19+
## Screenshot Masking
20+
21+
Screenshot masking allows you to mask sensitive data in screenshots before they are captured. You can customize this behavior to fit your application's needs.
22+
23+
<Alert>
24+
25+
Screenshot masking requires the `sentry-android-replay` module at runtime. This module is included by default if you use the `sentry-android` dependency. If you only depend on `sentry-android-core`, add the replay module explicitly:
26+
27+
```groovy {filename:app/build.gradle}
28+
dependencies {
29+
implementation 'io.sentry:sentry-android-replay:{{@inject packages.version('sentry.java.android', '8.34.0') }}'
30+
}
31+
```
32+
33+
```kotlin {filename:app/build.gradle.kts}
34+
dependencies {
35+
implementation("io.sentry:sentry-android-replay:{{@inject packages.version('sentry.java.android', '8.34.0') }}")
36+
}
37+
```
38+
39+
If masking options are configured but the module is not available at runtime, the SDK will skip capturing screenshots entirely to avoid leaking sensitive data.
40+
41+
</Alert>
42+
43+
### Default Masking Behavior
44+
45+
Unlike Session Replay, screenshot masking is **disabled by default**. You can enable masking for all text and image content:
46+
47+
```java
48+
import io.sentry.android.core.SentryAndroid;
49+
50+
SentryAndroid.init(this, options -> {
51+
options.getScreenshot().setMaskAllText(true);
52+
options.getScreenshot().setMaskAllImages(true);
53+
});
54+
```
55+
56+
```kotlin
57+
import io.sentry.android.core.SentryAndroid
58+
59+
SentryAndroid.init(this) { options ->
60+
options.screenshot.setMaskAllText(true)
61+
options.screenshot.setMaskAllImages(true)
62+
}
63+
```
64+
65+
```xml {filename:AndroidManifest.xml}
66+
<application>
67+
<meta-data android:name="io.sentry.screenshot.mask-all-text" android:value="true" />
68+
<meta-data android:name="io.sentry.screenshot.mask-all-images" android:value="true" />
69+
</application>
70+
```
71+
72+
<Alert>
73+
74+
When `setMaskAllImages(true)` is set, the SDK will also mask `WebView`, `VideoView`, CameraX `PreviewView`, ExoPlayer, and Media3 player views in addition to `ImageView`.
75+
76+
</Alert>
77+
78+
### Mask by View Class
79+
80+
You can choose which type of view to mask or unmask by using `addMaskViewClass` or `addUnmaskViewClass`. These accept fully-qualified class names and apply to all subclasses of the specified class as well.
81+
82+
```java
83+
import io.sentry.android.core.SentryAndroid;
84+
85+
SentryAndroid.init(this, options -> {
86+
options.getScreenshot().addMaskViewClass("com.example.MyCustomView");
87+
options.getScreenshot().addUnmaskViewClass("com.example.PublicLabel");
88+
});
89+
```
90+
91+
```kotlin
92+
import io.sentry.android.core.SentryAndroid
93+
94+
SentryAndroid.init(this) { options ->
95+
options.screenshot.addMaskViewClass("com.example.MyCustomView")
96+
options.screenshot.addUnmaskViewClass("com.example.PublicLabel")
97+
}
98+
```
99+
100+
<Alert>
101+
102+
If you use R8/ProGuard, make sure to keep the class names you reference in masking rules. Obfuscated class names will not match at runtime.
103+
104+
</Alert>
105+
106+
### Mask by View Instance
107+
108+
You can mask or unmask specific view instances using extension functions from the `sentry-android-replay` module:
109+
110+
```kotlin
111+
import io.sentry.android.replay.sentryReplayMask
112+
import io.sentry.android.replay.sentryReplayUnmask
113+
114+
myTextView.sentryReplayMask()
115+
myImageView.sentryReplayUnmask()
116+
```
117+
118+
You can also set masking via XML layout tags:
119+
120+
```xml
121+
<TextView
122+
android:layout_width="wrap_content"
123+
android:layout_height="wrap_content"
124+
android:text="Sensitive data">
125+
<tag android:id="@id/sentry_privacy" android:value="mask" />
126+
</TextView>
127+
```
128+
129+
Alternatively, you can use the `android:tag` attribute with the `sentry-mask` or `sentry-unmask` string values:
130+
131+
```xml
132+
<TextView
133+
android:layout_width="wrap_content"
134+
android:layout_height="wrap_content"
135+
android:tag="sentry-mask"
136+
android:text="Sensitive data" />
137+
```
138+
139+
In Java, you can use view tags with the `sentry_privacy` resource ID:
140+
141+
```java
142+
import io.sentry.android.replay.R;
143+
144+
view.setTag(R.id.sentry_privacy, "mask");
145+
view.setTag(R.id.sentry_privacy, "unmask");
146+
```
147+
148+
### Jetpack Compose
149+
150+
For Jetpack Compose, use the `sentryReplayMask()` and `sentryReplayUnmask()` modifiers:
151+
152+
```kotlin
153+
import io.sentry.android.replay.sentryReplayMask
154+
import io.sentry.android.replay.sentryReplayUnmask
155+
156+
@Composable
157+
fun MyScreen() {
158+
Column {
159+
Text(
160+
text = "Sensitive info",
161+
modifier = Modifier.sentryReplayMask()
162+
)
163+
Text(
164+
text = "Public info",
165+
modifier = Modifier.sentryReplayUnmask()
166+
)
167+
}
168+
}
169+
```
170+
171+
### General Masking Rules
172+
173+
#### View Groups
174+
175+
- If a `ViewGroup` is marked as masked, **all its child views will also be masked**, even if some children would normally not be masked. This prioritizes safety and ensures no sensitive information is unintentionally exposed.
176+
177+
- If a `ViewGroup` is marked as unmasked, **its child views don't automatically inherit this behavior**. You'll need to explicitly mark each child view as unmasked if you want them to appear unmasked.
178+
179+
#### Masking Priority
180+
181+
Masking and unmasking rules are applied in the following order:
182+
183+
1. Check if a view is marked as `unmasked` via a tag/extension or modifier.
184+
2. Check if a view is marked as `masked` via a tag/extension or modifier.
185+
3. Check if a view's class is marked as unmasked via `addUnmaskViewClass`.
186+
4. Check if a view's class is marked as masked via `addMaskViewClass`.
187+
19188
## Viewing Screenshots
20189

21190
If one is available, you'll see a thumbnail of the screenshot when you click on a specific issue from the [**Issues**](https://demo.sentry.io/issues/) page.

platform-includes/enriching-events/attach-screenshots/android.mdx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
</application>
55
```
66

7+
<Alert level="warning">
8+
9+
Before enabling screenshots in production, verify your masking configuration to ensure no sensitive data is captured. If you find any masking issues or sensitive data that should be masked but isn't, please [create a GitHub issue](https://github.com/getsentry/sentry-java/issues/new/choose) and avoid deploying to production with screenshots enabled until the issue is resolved.
10+
11+
</Alert>
12+
713
### Customize Screenshot Capturing
814

915
<Alert>

0 commit comments

Comments
 (0)