Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,27 @@

- Add support for `feedback` envelope header item type ([#3687](https://github.com/getsentry/sentry-java/pull/3687))
- Add breadcrumb.origin field ([#3727](https://github.com/getsentry/sentry-java/pull/3727))
- Session Replay: Add options to selectively mask/unmask views captured in replay. The following options are available: ([#3689](https://github.com/getsentry/sentry-java/pull/3689))
- `android:tag="sentry-mask|sentry-unmask"` in XML or `view.setTag("sentry-mask|sentry-unmask")` in code tags
- if you already have a tag set for a view, you can set a tag by id: `<tag android:id="@id/sentry_privacy" android:value="mask|unmask"/>` in XML or `view.setTag(io.sentry.android.replay.R.id.sentry_privacy, "mask|unmask")` in code
- `view.sentryReplayMask()` or `view.sentryReplayUnmask()` extension functions
- mask/unmask `View`s of a certain type by adding fully-qualified classname to one of the lists `options.experimental.sessionReplay.addMaskViewClass()` or `options.experimental.sessionReplay.addUnmaskViewClass()`. Note, that all of the view subclasses/subtypes will be masked/unmasked as well
- For example, (this is already a default behavior) to mask all `TextView`s and their subclasses (`RadioButton`, `EditText`, etc.): `options.experimental.sessionReplay.addMaskViewClass("android.widget.TextView")`
- If you're using code obfuscation, adjust your proguard-rules accordingly, so your custom view class name is not minified
- Session Replay: Support Jetpack Compose masking ([#3739](https://github.com/getsentry/sentry-java/pull/3739))
- To selectively mask/unmask @Composables, use `Modifier.sentryReplayMask()` and `Modifier.sentryReplayUnmask()` modifiers

### Fixes

- Avoid stopping appStartProfiler after application creation ([#3630](https://github.com/getsentry/sentry-java/pull/3630))
- Session Replay: Correctly detect dominant color for `TextView`s with Spans ([#3682](https://github.com/getsentry/sentry-java/pull/3682))
- Session Replay: Add options to selectively redact/ignore views from being captured. The following options are available: ([#3689](https://github.com/getsentry/sentry-java/pull/3689))
- `android:tag="sentry-redact|sentry-ignore"` in XML or `view.setTag("sentry-redact|sentry-ignore")` in code tags
- if you already have a tag set for a view, you can set a tag by id: `<tag android:id="@id/sentry_privacy" android:value="redact|ignore"/>` in XML or `view.setTag(io.sentry.android.replay.R.id.sentry_privacy, "redact|ignore")` in code
- `view.sentryReplayRedact()` or `view.sentryReplayIgnore()` extension functions
- redact/ignore `View`s of a certain type by adding fully-qualified classname to one of the lists `options.experimental.sessionReplay.addRedactViewClass()` or `options.experimental.sessionReplay.addIgnoreViewClass()`. Note, that all of the view subclasses/subtypes will be redacted/ignored as well
- For example, (this is already a default behavior) to redact all `TextView`s and their subclasses (`RadioButton`, `EditText`, etc.): `options.experimental.sessionReplay.addRedactViewClass("android.widget.TextView")`
- If you're using code obfuscation, adjust your proguard-rules accordingly, so your custom view class name is not minified
- Fix ensure Application Context is used even when SDK is initialized via Activity Context ([#3669](https://github.com/getsentry/sentry-java/pull/3669))

*Breaking changes*:

- `options.experimental.sessionReplay.errorSampleRate` was renamed to `options.experimental.sessionReplay.onErrorSampleRate` ([#3637](https://github.com/getsentry/sentry-java/pull/3637))
- Manifest option `io.sentry.session-replay.error-sample-rate` was renamed to `io.sentry.session-replay.on-error-sample-rate` ([#3637](https://github.com/getsentry/sentry-java/pull/3637))
- Change `redactAllText` and `redactAllImages` to `maskAllText` and `maskAllImages` ([#3741](https://github.com/getsentry/sentry-java/pull/3741))

## 7.14.0

Expand Down
3 changes: 3 additions & 0 deletions buildSrc/src/main/java/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -147,8 +147,11 @@ object Config {
val composeActivity = "androidx.activity:activity-compose:1.4.0"
val composeFoundation = "androidx.compose.foundation:foundation:$composeVersion"
val composeUi = "androidx.compose.ui:ui:$composeVersion"

val composeUiReplay = "androidx.compose.ui:ui:1.5.0" // Note: don't change without testing forwards compatibility
val composeFoundationLayout = "androidx.compose.foundation:foundation-layout:$composeVersion"
val composeMaterial = "androidx.compose.material3:material3:1.0.0-alpha13"
val composeCoil = "io.coil-kt:coil-compose:2.6.0"

val apolloKotlin = "com.apollographql.apollo3:apollo-runtime:3.8.2"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ final class ManifestMetadataReader {

static final String REPLAYS_ERROR_SAMPLE_RATE = "io.sentry.session-replay.on-error-sample-rate";

static final String REPLAYS_REDACT_ALL_TEXT = "io.sentry.session-replay.redact-all-text";
static final String REPLAYS_MASK_ALL_TEXT = "io.sentry.session-replay.mask-all-text";

static final String REPLAYS_REDACT_ALL_IMAGES = "io.sentry.session-replay.redact-all-images";
static final String REPLAYS_MASK_ALL_IMAGES = "io.sentry.session-replay.mask-all-images";

/** ManifestMetadataReader ctor */
private ManifestMetadataReader() {}
Expand Down Expand Up @@ -409,12 +409,12 @@ static void applyMetadata(
options
.getExperimental()
.getSessionReplay()
.setRedactAllText(readBool(metadata, logger, REPLAYS_REDACT_ALL_TEXT, true));
.setMaskAllText(readBool(metadata, logger, REPLAYS_MASK_ALL_TEXT, true));

options
.getExperimental()
.getSessionReplay()
.setRedactAllImages(readBool(metadata, logger, REPLAYS_REDACT_ALL_IMAGES, true));
.setMaskAllImages(readBool(metadata, logger, REPLAYS_MASK_ALL_IMAGES, true));
}

options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1465,29 +1465,29 @@ class ManifestMetadataReaderTest {
}

@Test
fun `applyMetadata reads session replay redact flags to options`() {
fun `applyMetadata reads session replay mask flags to options`() {
// Arrange
val bundle = bundleOf(ManifestMetadataReader.REPLAYS_REDACT_ALL_TEXT to false, ManifestMetadataReader.REPLAYS_REDACT_ALL_IMAGES to false)
val bundle = bundleOf(ManifestMetadataReader.REPLAYS_MASK_ALL_TEXT to false, ManifestMetadataReader.REPLAYS_MASK_ALL_IMAGES to false)
val context = fixture.getContext(metaData = bundle)

// Act
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
assertTrue(fixture.options.experimental.sessionReplay.ignoreViewClasses.contains(SentryReplayOptions.IMAGE_VIEW_CLASS_NAME))
assertTrue(fixture.options.experimental.sessionReplay.ignoreViewClasses.contains(SentryReplayOptions.TEXT_VIEW_CLASS_NAME))
assertTrue(fixture.options.experimental.sessionReplay.unmaskViewClasses.contains(SentryReplayOptions.IMAGE_VIEW_CLASS_NAME))
assertTrue(fixture.options.experimental.sessionReplay.unmaskViewClasses.contains(SentryReplayOptions.TEXT_VIEW_CLASS_NAME))
}

@Test
fun `applyMetadata reads session replay redact flags to options and keeps default if not found`() {
fun `applyMetadata reads session replay mask flags to options and keeps default if not found`() {
// Arrange
val context = fixture.getContext()

// Act
ManifestMetadataReader.applyMetadata(context, fixture.options, fixture.buildInfoProvider)

// Assert
assertTrue(fixture.options.experimental.sessionReplay.redactViewClasses.contains(SentryReplayOptions.IMAGE_VIEW_CLASS_NAME))
assertTrue(fixture.options.experimental.sessionReplay.redactViewClasses.contains(SentryReplayOptions.TEXT_VIEW_CLASS_NAME))
assertTrue(fixture.options.experimental.sessionReplay.maskViewClasses.contains(SentryReplayOptions.IMAGE_VIEW_CLASS_NAME))
assertTrue(fixture.options.experimental.sessionReplay.maskViewClasses.contains(SentryReplayOptions.TEXT_VIEW_CLASS_NAME))
}
}
68 changes: 58 additions & 10 deletions sentry-android-replay/api/sentry-android-replay.api
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ public final class io/sentry/android/replay/BuildConfig {
}

public class io/sentry/android/replay/DefaultReplayBreadcrumbConverter : io/sentry/ReplayBreadcrumbConverter {
public static final field $stable I
public fun <init> ()V
public fun convert (Lio/sentry/Breadcrumb;)Lio/sentry/rrweb/RRWebEvent;
}

public final class io/sentry/android/replay/GeneratedVideo {
public static final field $stable I
public fun <init> (Ljava/io/File;IJ)V
public final fun component1 ()Ljava/io/File;
public final fun component2 ()I
Expand All @@ -26,6 +28,11 @@ public final class io/sentry/android/replay/GeneratedVideo {
public fun toString ()Ljava/lang/String;
}

public final class io/sentry/android/replay/ModifierExtensionsKt {
public static final fun sentryReplayMask (Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
public static final fun sentryReplayUnmask (Landroidx/compose/ui/Modifier;)Landroidx/compose/ui/Modifier;
}

public abstract interface class io/sentry/android/replay/Recorder : java/io/Closeable {
public abstract fun pause ()V
public abstract fun resume ()V
Expand All @@ -34,6 +41,7 @@ public abstract interface class io/sentry/android/replay/Recorder : java/io/Clos
}

public final class io/sentry/android/replay/ReplayCache : java/io/Closeable {
public static final field $stable I
public static final field Companion Lio/sentry/android/replay/ReplayCache$Companion;
public fun <init> (Lio/sentry/SentryOptions;Lio/sentry/protocol/SentryId;Lio/sentry/android/replay/ScreenshotRecorderConfig;)V
public final fun addFrame (Ljava/io/File;JLjava/lang/String;)V
Expand All @@ -50,6 +58,7 @@ public final class io/sentry/android/replay/ReplayCache$Companion {
}

public final class io/sentry/android/replay/ReplayIntegration : android/content/ComponentCallbacks, io/sentry/Integration, io/sentry/ReplayController, io/sentry/android/replay/ScreenshotRecorderCallback, io/sentry/android/replay/gestures/TouchRecorderCallback, java/io/Closeable {
public static final field $stable I
public fun <init> (Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;)V
public fun <init> (Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;)V
public synthetic fun <init> (Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function2;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand Down Expand Up @@ -78,6 +87,7 @@ public abstract interface class io/sentry/android/replay/ScreenshotRecorderCallb
}

public final class io/sentry/android/replay/ScreenshotRecorderConfig {
public static final field $stable I
public static final field Companion Lio/sentry/android/replay/ScreenshotRecorderConfig$Companion;
public fun <init> (IIFFII)V
public final fun component1 ()I
Expand All @@ -103,25 +113,33 @@ public final class io/sentry/android/replay/ScreenshotRecorderConfig$Companion {
public final fun from (Landroid/content/Context;Lio/sentry/SentryReplayOptions;)Lio/sentry/android/replay/ScreenshotRecorderConfig;
}

public final class io/sentry/android/replay/SentryReplayModifiers {
public static final field $stable I
public static final field INSTANCE Lio/sentry/android/replay/SentryReplayModifiers;
public final fun getSentryPrivacy ()Landroidx/compose/ui/semantics/SemanticsPropertyKey;
}

public final class io/sentry/android/replay/SessionReplayOptionsKt {
public static final fun getRedactAllImages (Lio/sentry/SentryReplayOptions;)Z
public static final fun getRedactAllText (Lio/sentry/SentryReplayOptions;)Z
public static final fun setRedactAllImages (Lio/sentry/SentryReplayOptions;Z)V
public static final fun setRedactAllText (Lio/sentry/SentryReplayOptions;Z)V
public static final fun getMaskAllImages (Lio/sentry/SentryReplayOptions;)Z
public static final fun getMaskAllText (Lio/sentry/SentryReplayOptions;)Z
public static final fun setMaskAllImages (Lio/sentry/SentryReplayOptions;Z)V
public static final fun setMaskAllText (Lio/sentry/SentryReplayOptions;Z)V
}

public final class io/sentry/android/replay/ViewExtensionsKt {
public static final fun sentryReplayIgnore (Landroid/view/View;)V
public static final fun sentryReplayRedact (Landroid/view/View;)V
public static final fun sentryReplayMask (Landroid/view/View;)V
public static final fun sentryReplayUnmask (Landroid/view/View;)V
}

public final class io/sentry/android/replay/gestures/GestureRecorder : io/sentry/android/replay/OnRootViewsChangedListener {
public static final field $stable I
public fun <init> (Lio/sentry/SentryOptions;Lio/sentry/android/replay/gestures/TouchRecorderCallback;)V
public fun onRootViewsChanged (Landroid/view/View;Z)V
public final fun stop ()V
}

public final class io/sentry/android/replay/gestures/ReplayGestureConverter {
public static final field $stable I
public fun <init> (Lio/sentry/transport/ICurrentDateProvider;)V
public final fun convert (Landroid/view/MotionEvent;Lio/sentry/android/replay/ScreenshotRecorderConfig;)Ljava/util/List;
}
Expand All @@ -130,6 +148,19 @@ public abstract interface class io/sentry/android/replay/gestures/TouchRecorderC
public abstract fun onTouchEvent (Landroid/view/MotionEvent;)V
}

public final class io/sentry/android/replay/util/AndroidTextLayout : io/sentry/android/replay/util/TextLayout {
public static final field $stable I
public fun <init> (Landroid/text/Layout;)V
public fun getDominantTextColor ()Ljava/lang/Integer;
public fun getEllipsisCount (I)I
public fun getLineBottom (I)I
public fun getLineCount ()I
public fun getLineStart (I)I
public fun getLineTop (I)I
public fun getLineVisibleEnd (I)I
public fun getPrimaryHorizontal (II)F
}

public class io/sentry/android/replay/util/FixedWindowCallback : android/view/Window$Callback {
public final field delegate Landroid/view/Window$Callback;
public fun <init> (Landroid/view/Window$Callback;)V
Expand Down Expand Up @@ -160,6 +191,17 @@ public class io/sentry/android/replay/util/FixedWindowCallback : android/view/Wi
public fun onWindowStartingActionMode (Landroid/view/ActionMode$Callback;I)Landroid/view/ActionMode;
}

public abstract interface class io/sentry/android/replay/util/TextLayout {
public abstract fun getDominantTextColor ()Ljava/lang/Integer;
public abstract fun getEllipsisCount (I)I
public abstract fun getLineBottom (I)I
public abstract fun getLineCount ()I
public abstract fun getLineStart (I)I
public abstract fun getLineTop (I)I
public abstract fun getLineVisibleEnd (I)I
public abstract fun getPrimaryHorizontal (II)F
}

public abstract interface class io/sentry/android/replay/video/SimpleFrameMuxer {
public abstract fun getVideoTime ()J
public abstract fun isStarted ()Z
Expand All @@ -169,6 +211,7 @@ public abstract interface class io/sentry/android/replay/video/SimpleFrameMuxer
}

public final class io/sentry/android/replay/video/SimpleMp4FrameMuxer : io/sentry/android/replay/video/SimpleFrameMuxer {
public static final field $stable I
public fun <init> (Ljava/lang/String;F)V
public fun getVideoTime ()J
public fun isStarted ()Z
Expand All @@ -178,6 +221,7 @@ public final class io/sentry/android/replay/video/SimpleMp4FrameMuxer : io/sentr
}

public abstract class io/sentry/android/replay/viewhierarchy/ViewHierarchyNode {
public static final field $stable I
public static final field Companion Lio/sentry/android/replay/viewhierarchy/ViewHierarchyNode$Companion;
public synthetic fun <init> (FFIIFILio/sentry/android/replay/viewhierarchy/ViewHierarchyNode;ZZZLandroid/graphics/Rect;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (FFIIFILio/sentry/android/replay/viewhierarchy/ViewHierarchyNode;ZZZLandroid/graphics/Rect;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
Expand All @@ -186,7 +230,7 @@ public abstract class io/sentry/android/replay/viewhierarchy/ViewHierarchyNode {
public final fun getElevation ()F
public final fun getHeight ()I
public final fun getParent ()Lio/sentry/android/replay/viewhierarchy/ViewHierarchyNode;
public final fun getShouldRedact ()Z
public final fun getShouldMask ()Z
public final fun getVisibleRect ()Landroid/graphics/Rect;
public final fun getWidth ()I
public final fun getX ()F
Expand All @@ -195,6 +239,7 @@ public abstract class io/sentry/android/replay/viewhierarchy/ViewHierarchyNode {
public final fun isObscured (Lio/sentry/android/replay/viewhierarchy/ViewHierarchyNode;)Z
public final fun isVisible ()Z
public final fun setChildren (Ljava/util/List;)V
public final fun setImportantForCaptureToAncestors (Z)V
public final fun setImportantForContentCapture (Z)V
public final fun traverse (Lkotlin/jvm/functions/Function1;)V
}
Expand All @@ -204,20 +249,23 @@ public final class io/sentry/android/replay/viewhierarchy/ViewHierarchyNode$Comp
}

public final class io/sentry/android/replay/viewhierarchy/ViewHierarchyNode$GenericViewHierarchyNode : io/sentry/android/replay/viewhierarchy/ViewHierarchyNode {
public static final field $stable I
public fun <init> (FFIIFILio/sentry/android/replay/viewhierarchy/ViewHierarchyNode;ZZZLandroid/graphics/Rect;)V
public synthetic fun <init> (FFIIFILio/sentry/android/replay/viewhierarchy/ViewHierarchyNode;ZZZLandroid/graphics/Rect;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
}

public final class io/sentry/android/replay/viewhierarchy/ViewHierarchyNode$ImageViewHierarchyNode : io/sentry/android/replay/viewhierarchy/ViewHierarchyNode {
public static final field $stable I
public fun <init> (FFIIFILio/sentry/android/replay/viewhierarchy/ViewHierarchyNode;ZZZLandroid/graphics/Rect;)V
public synthetic fun <init> (FFIIFILio/sentry/android/replay/viewhierarchy/ViewHierarchyNode;ZZZLandroid/graphics/Rect;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
}

public final class io/sentry/android/replay/viewhierarchy/ViewHierarchyNode$TextViewHierarchyNode : io/sentry/android/replay/viewhierarchy/ViewHierarchyNode {
public fun <init> (Landroid/text/Layout;Ljava/lang/Integer;IIFFIIFILio/sentry/android/replay/viewhierarchy/ViewHierarchyNode;ZZZLandroid/graphics/Rect;)V
public synthetic fun <init> (Landroid/text/Layout;Ljava/lang/Integer;IIFFIIFILio/sentry/android/replay/viewhierarchy/ViewHierarchyNode;ZZZLandroid/graphics/Rect;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public static final field $stable I
public fun <init> (Lio/sentry/android/replay/util/TextLayout;Ljava/lang/Integer;IIFFIIFILio/sentry/android/replay/viewhierarchy/ViewHierarchyNode;ZZZLandroid/graphics/Rect;)V
public synthetic fun <init> (Lio/sentry/android/replay/util/TextLayout;Ljava/lang/Integer;IIFFIIFILio/sentry/android/replay/viewhierarchy/ViewHierarchyNode;ZZZLandroid/graphics/Rect;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getDominantColor ()Ljava/lang/Integer;
public final fun getLayout ()Landroid/text/Layout;
public final fun getLayout ()Lio/sentry/android/replay/util/TextLayout;
public final fun getPaddingLeft ()I
public final fun getPaddingTop ()I
}
Expand Down
Loading