diff --git a/CHANGELOG.md b/CHANGELOG.md index 72431094bc..33deafe6e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ ### Internal +- Add integration to track session replay custom masking ([#5088](https://github.com/getsentry/sentry-java/pull/5088)) - Establish new native exception mechanisms to differentiate events generated by `sentry-native` from `ApplicationExitInfo`. ([#5052](https://github.com/getsentry/sentry-java/pull/5052)) - Set `write` permission for `statuses` in the changelog preview GHA workflow. ([#5053](https://github.com/getsentry/sentry-java/pull/5053)) diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt index cbcadc0391..dcef27d523 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ComposeViewHierarchyNode.kt @@ -85,10 +85,12 @@ internal object ComposeViewHierarchyNode { ): Boolean { val sentryPrivacyModifier = this?.getOrNull(SentryReplayModifiers.SentryPrivacy) if (sentryPrivacyModifier == "unmask") { + SentryReplayOptions.trackCustomMasking() return false } if (sentryPrivacyModifier == "mask") { + SentryReplayOptions.trackCustomMasking() return true } diff --git a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ViewHierarchyNode.kt b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ViewHierarchyNode.kt index 2cde60f4fb..08add79d62 100644 --- a/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ViewHierarchyNode.kt +++ b/sentry-android-replay/src/main/java/io/sentry/android/replay/viewhierarchy/ViewHierarchyNode.kt @@ -8,6 +8,7 @@ import android.view.ViewParent import android.widget.ImageView import android.widget.TextView import io.sentry.SentryOptions +import io.sentry.SentryReplayOptions import io.sentry.android.replay.R import io.sentry.android.replay.util.AndroidTextLayout import io.sentry.android.replay.util.TextLayout @@ -291,6 +292,7 @@ internal sealed class ViewHierarchyNode( (tag as? String)?.lowercase()?.contains(SENTRY_UNMASK_TAG) == true || getTag(R.id.sentry_privacy) == "unmask" ) { + SentryReplayOptions.trackCustomMasking() return false } @@ -298,6 +300,7 @@ internal sealed class ViewHierarchyNode( (tag as? String)?.lowercase()?.contains(SENTRY_MASK_TAG) == true || getTag(R.id.sentry_privacy) == "mask" ) { + SentryReplayOptions.trackCustomMasking() return true } diff --git a/sentry/src/main/java/io/sentry/SentryReplayOptions.java b/sentry/src/main/java/io/sentry/SentryReplayOptions.java index 367b150c92..c4e112f8b2 100644 --- a/sentry/src/main/java/io/sentry/SentryReplayOptions.java +++ b/sentry/src/main/java/io/sentry/SentryReplayOptions.java @@ -1,5 +1,7 @@ package io.sentry; +import static io.sentry.util.IntegrationUtils.addIntegrationToSdkVersion; + import io.sentry.protocol.SdkVersion; import io.sentry.util.SampleRateUtils; import java.util.ArrayList; @@ -13,9 +15,13 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; public final class SentryReplayOptions { + private static final String CUSTOM_MASKING_INTEGRATION_NAME = "ReplayCustomMasking"; + private static volatile boolean customMaskingTracked = false; + public static final String TEXT_VIEW_CLASS_NAME = "android.widget.TextView"; public static final String IMAGE_VIEW_CLASS_NAME = "android.widget.ImageView"; public static final String WEB_VIEW_CLASS_NAME = "android.webkit.WebView"; @@ -209,8 +215,9 @@ public enum SentryReplayQuality { public SentryReplayOptions(final boolean empty, final @Nullable SdkVersion sdkVersion) { if (!empty) { - setMaskAllText(true); - setMaskAllImages(true); + // Add default mask classes directly without setting usingCustomMasking flag + maskViewClasses.add(TEXT_VIEW_CLASS_NAME); + maskViewClasses.add(IMAGE_VIEW_CLASS_NAME); maskViewClasses.add(WEB_VIEW_CLASS_NAME); maskViewClasses.add(VIDEO_VIEW_CLASS_NAME); maskViewClasses.add(ANDROIDX_MEDIA_VIEW_CLASS_NAME); @@ -275,11 +282,12 @@ public void setSessionSampleRate(final @Nullable Double sessionSampleRate) { *
Default is enabled. */ public void setMaskAllText(final boolean maskAllText) { + trackCustomMasking(); if (maskAllText) { - addMaskViewClass(TEXT_VIEW_CLASS_NAME); + maskViewClasses.add(TEXT_VIEW_CLASS_NAME); unmaskViewClasses.remove(TEXT_VIEW_CLASS_NAME); } else { - addUnmaskViewClass(TEXT_VIEW_CLASS_NAME); + unmaskViewClasses.add(TEXT_VIEW_CLASS_NAME); maskViewClasses.remove(TEXT_VIEW_CLASS_NAME); } } @@ -293,11 +301,12 @@ public void setMaskAllText(final boolean maskAllText) { *
Default is enabled.
*/
public void setMaskAllImages(final boolean maskAllImages) {
+ trackCustomMasking();
if (maskAllImages) {
- addMaskViewClass(IMAGE_VIEW_CLASS_NAME);
+ maskViewClasses.add(IMAGE_VIEW_CLASS_NAME);
unmaskViewClasses.remove(IMAGE_VIEW_CLASS_NAME);
} else {
- addUnmaskViewClass(IMAGE_VIEW_CLASS_NAME);
+ unmaskViewClasses.add(IMAGE_VIEW_CLASS_NAME);
maskViewClasses.remove(IMAGE_VIEW_CLASS_NAME);
}
}
@@ -308,6 +317,7 @@ public Set