Skip to content

Commit 8a44963

Browse files
runningcodeclaude
andcommitted
refactor(replay): Rename ReplaySnapshotObserver to ReplayFrameObserver (JAVA-504)
Rename the interface to ReplayFrameObserver and the callback method to onMaskedFrameCaptured to clarify that frames have masking applied. Also update the changelog with a usage snippet. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent a7a01d9 commit 8a44963

7 files changed

Lines changed: 49 additions & 30 deletions

File tree

CHANGELOG.md

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,24 @@
55
### Features
66

77
- Add support to configure reporting historical ANRs via `AndroidManifest.xml` using the `io.sentry.anr.report-historical` attribute ([#5387](https://github.com/getsentry/sentry-java/pull/5387))
8-
- Session Replay: Add `ReplaySnapshotObserver` for observing captured replay frames ([#5386](https://github.com/getsentry/sentry-java/pull/5386))
8+
- Session Replay: Add `ReplayFrameObserver` for observing captured replay frames ([#5386](https://github.com/getsentry/sentry-java/pull/5386))
9+
10+
```kotlin
11+
SentryAndroid.init(context) { options ->
12+
options.sessionReplay.frameObserver =
13+
SentryReplayOptions.ReplayFrameObserver { hint, frameTimestamp, screenName ->
14+
val bitmap = hint.getAs(TypeCheckHint.REPLAY_FRAME_BITMAP, Bitmap::class.java)
15+
if (bitmap != null) {
16+
try {
17+
// Process the masked replay frame
18+
myAnalyzer.processFrame(bitmap, frameTimestamp, screenName)
19+
} finally {
20+
bitmap.recycle()
21+
}
22+
}
23+
}
24+
}
25+
```
926

1027
### Dependencies
1128

sentry-android-integration-tests/sentry-uitest-android/src/androidTestReplay/java/io/sentry/uitest/android/ReplaySnapshotTest.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ class ReplaySnapshotTest : BaseUiTest() {
4444

4545
initSentry {
4646
it.sessionReplay.sessionSampleRate = 1.0
47-
it.sessionReplay.snapshotObserver =
48-
SentryReplayOptions.ReplaySnapshotObserver { hint, frameTimestamp, screenName ->
47+
it.sessionReplay.frameObserver =
48+
SentryReplayOptions.ReplayFrameObserver { hint, frameTimestamp, screenName ->
4949
val bitmap =
5050
hint.getAs(TypeCheckHint.REPLAY_FRAME_BITMAP, Bitmap::class.java)
51-
?: return@ReplaySnapshotObserver
51+
?: return@ReplayFrameObserver
5252
val name = screenName ?: "unknown"
5353
if (capturedScreens.add(name)) {
5454
val file = File(snapshotsDir, "${name}_$frameTimestamp.png")

sentry-android-replay/src/main/java/io/sentry/android/replay/ReplayIntegration.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,16 +311,16 @@ public class ReplayIntegration(
311311
var screen: String? = null
312312
scopes?.configureScope { screen = it.screen?.substringAfterLast('.') }
313313
captureStrategy?.onScreenshotRecorded(bitmap) { frameTimeStamp ->
314-
val observer = options.sessionReplay.snapshotObserver
314+
val observer = options.sessionReplay.frameObserver
315315
if (observer != null) {
316316
val copy = bitmap.copy(bitmap.config!!, false)
317317
if (copy != null) {
318318
try {
319319
val hint = Hint()
320320
hint.set(TypeCheckHint.REPLAY_FRAME_BITMAP, copy)
321-
observer.onSnapshotCaptured(hint, frameTimeStamp, screen)
321+
observer.onMaskedFrameCaptured(hint, frameTimeStamp, screen)
322322
} catch (e: Throwable) {
323-
options.logger.log(ERROR, "Error in ReplaySnapshotObserver", e)
323+
options.logger.log(ERROR, "Error in ReplayFrameObserver", e)
324324
copy.recycle()
325325
}
326326
}

sentry-android-replay/src/test/java/io/sentry/android/replay/ReplayIntegrationTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -996,8 +996,8 @@ class ReplayIntegrationTest {
996996
replay.register(fixture.scopes, fixture.options)
997997
replay.start()
998998

999-
fixture.options.sessionReplay.snapshotObserver =
1000-
SentryReplayOptions.ReplaySnapshotObserver { hint, frameTimestamp, screenName ->
999+
fixture.options.sessionReplay.frameObserver =
1000+
SentryReplayOptions.ReplayFrameObserver { hint, frameTimestamp, screenName ->
10011001
callbackInvoked = true
10021002
receivedTimestamp = frameTimestamp
10031003
receivedScreen = screenName
@@ -1036,8 +1036,8 @@ class ReplayIntegrationTest {
10361036
replay.register(fixture.scopes, fixture.options)
10371037
replay.start()
10381038

1039-
fixture.options.sessionReplay.snapshotObserver =
1040-
SentryReplayOptions.ReplaySnapshotObserver { _, _, _ -> throw RuntimeException("test") }
1039+
fixture.options.sessionReplay.frameObserver =
1040+
SentryReplayOptions.ReplayFrameObserver { _, _, _ -> throw RuntimeException("test") }
10411041

10421042
val sourceBitmap =
10431043
mock<Bitmap> {

sentry/api/sentry.api

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4056,6 +4056,7 @@ public final class io/sentry/SentryReplayOptions : io/sentry/SentryMaskingOption
40564056
public fun addUnmaskViewClass (Ljava/lang/String;)V
40574057
public fun getBeforeErrorSampling ()Lio/sentry/SentryReplayOptions$BeforeErrorSamplingCallback;
40584058
public fun getErrorReplayDuration ()J
4059+
public fun getFrameObserver ()Lio/sentry/SentryReplayOptions$ReplayFrameObserver;
40594060
public fun getFrameRate ()I
40604061
public fun getNetworkDetailAllowUrls ()Ljava/util/List;
40614062
public fun getNetworkDetailDenyUrls ()Ljava/util/List;
@@ -4069,7 +4070,6 @@ public final class io/sentry/SentryReplayOptions : io/sentry/SentryMaskingOption
40694070
public fun getSessionDuration ()J
40704071
public fun getSessionSampleRate ()Ljava/lang/Double;
40714072
public fun getSessionSegmentDuration ()J
4072-
public fun getSnapshotObserver ()Lio/sentry/SentryReplayOptions$ReplaySnapshotObserver;
40734073
public fun isCaptureSurfaceViews ()Z
40744074
public fun isDebug ()Z
40754075
public fun isNetworkCaptureBodies ()Z
@@ -4079,6 +4079,7 @@ public final class io/sentry/SentryReplayOptions : io/sentry/SentryMaskingOption
40794079
public fun setBeforeErrorSampling (Lio/sentry/SentryReplayOptions$BeforeErrorSamplingCallback;)V
40804080
public fun setCaptureSurfaceViews (Z)V
40814081
public fun setDebug (Z)V
4082+
public fun setFrameObserver (Lio/sentry/SentryReplayOptions$ReplayFrameObserver;)V
40824083
public fun setMaskAllImages (Z)V
40834084
public fun setMaskAllText (Z)V
40844085
public fun setNetworkCaptureBodies (Z)V
@@ -4091,7 +4092,6 @@ public final class io/sentry/SentryReplayOptions : io/sentry/SentryMaskingOption
40914092
public fun setScreenshotStrategy (Lio/sentry/ScreenshotStrategyType;)V
40924093
public fun setSdkVersion (Lio/sentry/protocol/SdkVersion;)V
40934094
public fun setSessionSampleRate (Ljava/lang/Double;)V
4094-
public fun setSnapshotObserver (Lio/sentry/SentryReplayOptions$ReplaySnapshotObserver;)V
40954095
public fun setTrackConfiguration (Z)V
40964096
public fun trackCustomMasking ()V
40974097
}
@@ -4100,8 +4100,8 @@ public abstract interface class io/sentry/SentryReplayOptions$BeforeErrorSamplin
41004100
public abstract fun execute (Lio/sentry/SentryEvent;Lio/sentry/Hint;)Z
41014101
}
41024102

4103-
public abstract interface class io/sentry/SentryReplayOptions$ReplaySnapshotObserver {
4104-
public abstract fun onSnapshotCaptured (Lio/sentry/Hint;JLjava/lang/String;)V
4103+
public abstract interface class io/sentry/SentryReplayOptions$ReplayFrameObserver {
4104+
public abstract fun onMaskedFrameCaptured (Lio/sentry/Hint;JLjava/lang/String;)V
41054105
}
41064106

41074107
public final class io/sentry/SentryReplayOptions$SentryReplayQuality : java/lang/Enum {

sentry/src/main/java/io/sentry/SentryReplayOptions.java

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ public interface BeforeErrorSamplingCallback {
3737
}
3838

3939
/**
40-
* Observer that is notified when a replay snapshot is captured. The snapshot bitmap (with masking
41-
* applied) is passed via a {@link Hint} using the key {@link TypeCheckHint#REPLAY_FRAME_BITMAP}.
40+
* Observer that is notified when a masked replay frame is captured. The frame bitmap (with
41+
* masking already applied) is passed via a {@link Hint} using the key {@link
42+
* TypeCheckHint#REPLAY_FRAME_BITMAP}.
4243
*
4344
* <p>On Android, retrieve the bitmap with: {@code hint.getAs(TypeCheckHint.REPLAY_FRAME_BITMAP,
4445
* Bitmap.class)}.
@@ -47,15 +48,15 @@ public interface BeforeErrorSamplingCallback {
4748
* the caller. Call {@code Bitmap.recycle()} when done to free native memory.
4849
*/
4950
@ApiStatus.Experimental
50-
public interface ReplaySnapshotObserver {
51+
public interface ReplayFrameObserver {
5152
/**
52-
* Called when a replay snapshot is captured.
53+
* Called when a masked replay frame is captured.
5354
*
5455
* @param hint contains the frame bitmap under {@link TypeCheckHint#REPLAY_FRAME_BITMAP}
5556
* @param frameTimestamp the timestamp (in milliseconds since epoch) when the frame was captured
5657
* @param screenName the current screen name, or {@code null} if unknown
5758
*/
58-
void onSnapshotCaptured(@NotNull Hint hint, long frameTimestamp, @Nullable String screenName);
59+
void onMaskedFrameCaptured(@NotNull Hint hint, long frameTimestamp, @Nullable String screenName);
5960
}
6061

6162
private static final String CUSTOM_MASKING_INTEGRATION_NAME = "ReplayCustomMasking";
@@ -233,7 +234,7 @@ public enum SentryReplayQuality {
233234
*/
234235
private @Nullable BeforeErrorSamplingCallback beforeErrorSampling;
235236

236-
@ApiStatus.Experimental private @Nullable ReplaySnapshotObserver snapshotObserver;
237+
@ApiStatus.Experimental private @Nullable ReplayFrameObserver frameObserver;
237238

238239
public SentryReplayOptions(final boolean empty, final @Nullable SdkVersion sdkVersion) {
239240
if (!empty) {
@@ -576,23 +577,24 @@ public void setBeforeErrorSampling(
576577
}
577578

578579
/**
579-
* Gets the observer that is notified when a replay snapshot is captured.
580+
* Gets the observer that is notified when a masked replay frame is captured.
580581
*
581582
* @return the observer, or {@code null} if not set
582583
*/
583584
@ApiStatus.Experimental
584-
public @Nullable ReplaySnapshotObserver getSnapshotObserver() {
585-
return snapshotObserver;
585+
public @Nullable ReplayFrameObserver getFrameObserver() {
586+
return frameObserver;
586587
}
587588

588589
/**
589-
* Sets the observer that is notified when a replay snapshot is captured. The frame bitmap is
590-
* passed via a {@link Hint} using the key {@link TypeCheckHint#REPLAY_FRAME_BITMAP}.
590+
* Sets the observer that is notified when a masked replay frame is captured. The frame bitmap
591+
* (with masking already applied) is passed via a {@link Hint} using the key {@link
592+
* TypeCheckHint#REPLAY_FRAME_BITMAP}.
591593
*
592-
* @param snapshotObserver the observer, or {@code null} to clear
594+
* @param frameObserver the observer, or {@code null} to clear
593595
*/
594596
@ApiStatus.Experimental
595-
public void setSnapshotObserver(final @Nullable ReplaySnapshotObserver snapshotObserver) {
596-
this.snapshotObserver = snapshotObserver;
597+
public void setFrameObserver(final @Nullable ReplayFrameObserver frameObserver) {
598+
this.frameObserver = frameObserver;
597599
}
598600
}

sentry/src/main/java/io/sentry/TypeCheckHint.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,6 @@ public final class TypeCheckHint {
141141
/** Used for Ktor Request breadcrumbs. */
142142
public static final String KTOR_CLIENT_REQUEST = "ktorClient:request";
143143

144-
/** Used for Session Replay frame bitmaps in the ReplaySnapshotObserver callback. */
144+
/** Used for Session Replay frame bitmaps in the ReplayFrameObserver callback. */
145145
public static final String REPLAY_FRAME_BITMAP = "replay:frameBitmap";
146146
}

0 commit comments

Comments
 (0)