Skip to content

Commit f8b9b25

Browse files
committed
Revert "Determine recording size based on active window (#4354)"
This reverts commit 307b057.
1 parent ab1a12c commit f8b9b25

File tree

22 files changed

+193
-396
lines changed

22 files changed

+193
-396
lines changed

CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
- Session Replay: Fix crash on devices with the Unisoc/Spreadtrum T606 chipset ([#4477](https://github.com/getsentry/sentry-java/pull/4477))
1616
- Session Replay: Fix masking of non-styled `Text` Composables ([#4361](https://github.com/getsentry/sentry-java/pull/4361))
1717
- Session Replay: Fix masking read-only `TextField` Composables ([#4362](https://github.com/getsentry/sentry-java/pull/4362))
18-
- Correctly capture Dialogs and non full-sized windows ([#4354](https://github.com/getsentry/sentry-java/pull/4354))
1918
- Fix Session Replay masking for newer versions of Jetpack Compose (1.8+) ([#4485](https://github.com/getsentry/sentry-java/pull/4485))
2019
- Session Replay: Expand fix for crash on devices to all Unisoc/Spreadtrum chipsets ([#4510](https://github.com/getsentry/sentry-java/pull/4510))
2120

sentry-android-replay/api/sentry-android-replay.api

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,9 @@ public final class io/sentry/android/replay/ModifierExtensionsKt {
3434
}
3535

3636
public abstract interface class io/sentry/android/replay/Recorder : java/io/Closeable {
37-
public abstract fun onConfigurationChanged (Lio/sentry/android/replay/ScreenshotRecorderConfig;)V
3837
public abstract fun pause ()V
39-
public abstract fun reset ()V
4038
public abstract fun resume ()V
41-
public abstract fun start ()V
39+
public abstract fun start (Lio/sentry/android/replay/ScreenshotRecorderConfig;)V
4240
public abstract fun stop ()V
4341
}
4442

@@ -59,24 +57,24 @@ public final class io/sentry/android/replay/ReplayCache$Companion {
5957
public final fun makeReplayCacheDir (Lio/sentry/SentryOptions;Lio/sentry/protocol/SentryId;)Ljava/io/File;
6058
}
6159

62-
public final class io/sentry/android/replay/ReplayIntegration : io/sentry/IConnectionStatusProvider$IConnectionStatusObserver, io/sentry/Integration, io/sentry/ReplayController, io/sentry/android/replay/ScreenshotRecorderCallback, io/sentry/android/replay/WindowCallback, io/sentry/android/replay/gestures/TouchRecorderCallback, io/sentry/transport/RateLimiter$IRateLimitObserver, java/io/Closeable {
60+
public final class io/sentry/android/replay/ReplayIntegration : android/content/ComponentCallbacks, io/sentry/IConnectionStatusProvider$IConnectionStatusObserver, io/sentry/Integration, io/sentry/ReplayController, io/sentry/android/replay/ScreenshotRecorderCallback, io/sentry/android/replay/gestures/TouchRecorderCallback, io/sentry/transport/RateLimiter$IRateLimitObserver, java/io/Closeable {
6361
public static final field $stable I
6462
public fun <init> (Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;)V
65-
public fun <init> (Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;)V
66-
public synthetic fun <init> (Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
63+
public fun <init> (Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;)V
64+
public synthetic fun <init> (Landroid/content/Context;Lio/sentry/transport/ICurrentDateProvider;Lkotlin/jvm/functions/Function0;Lkotlin/jvm/functions/Function1;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
6765
public fun captureReplay (Ljava/lang/Boolean;)V
6866
public fun close ()V
6967
public fun getBreadcrumbConverter ()Lio/sentry/ReplayBreadcrumbConverter;
7068
public final fun getReplayCacheDir ()Ljava/io/File;
7169
public fun getReplayId ()Lio/sentry/protocol/SentryId;
7270
public fun isRecording ()Z
73-
public final fun onConfigurationChanged (Lio/sentry/android/replay/ScreenshotRecorderConfig;)V
71+
public fun onConfigurationChanged (Landroid/content/res/Configuration;)V
7472
public fun onConnectionStatusChanged (Lio/sentry/IConnectionStatusProvider$ConnectionStatus;)V
73+
public fun onLowMemory ()V
7574
public fun onRateLimitChanged (Lio/sentry/transport/RateLimiter;)V
7675
public fun onScreenshotRecorded (Landroid/graphics/Bitmap;)V
7776
public fun onScreenshotRecorded (Ljava/io/File;J)V
7877
public fun onTouchEvent (Landroid/view/MotionEvent;)V
79-
public fun onWindowSizeChanged (II)V
8078
public fun pause ()V
8179
public fun register (Lio/sentry/IHub;Lio/sentry/SentryOptions;)V
8280
public fun resume ()V
@@ -114,7 +112,7 @@ public final class io/sentry/android/replay/ScreenshotRecorderConfig {
114112
}
115113

116114
public final class io/sentry/android/replay/ScreenshotRecorderConfig$Companion {
117-
public final fun fromSize (Landroid/content/Context;Lio/sentry/SentryReplayOptions;II)Lio/sentry/android/replay/ScreenshotRecorderConfig;
115+
public final fun from (Landroid/content/Context;Lio/sentry/SentryReplayOptions;)Lio/sentry/android/replay/ScreenshotRecorderConfig;
118116
}
119117

120118
public final class io/sentry/android/replay/SentryReplayModifiers {
@@ -135,10 +133,6 @@ public final class io/sentry/android/replay/ViewExtensionsKt {
135133
public static final fun sentryReplayUnmask (Landroid/view/View;)V
136134
}
137135

138-
public abstract interface class io/sentry/android/replay/WindowCallback {
139-
public abstract fun onWindowSizeChanged (II)V
140-
}
141-
142136
public final class io/sentry/android/replay/gestures/GestureRecorder : io/sentry/android/replay/OnRootViewsChangedListener {
143137
public static final field $stable I
144138
public fun <init> (Lio/sentry/SentryOptions;Lio/sentry/android/replay/gestures/TouchRecorderCallback;)V

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

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,11 @@ interface Recorder : Closeable {
88
* at which the screenshots should be taken, and the screenshots size/resolution, which can
99
* change e.g. in the case of orientation change or window size change
1010
*/
11-
fun start()
12-
13-
fun onConfigurationChanged(config: ScreenshotRecorderConfig)
11+
fun start(recorderConfig: ScreenshotRecorderConfig)
1412

1513
fun resume()
1614

1715
fun pause()
1816

19-
fun reset()
20-
2117
fun stop()
2218
}

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

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package io.sentry.android.replay
22

3+
import android.content.ComponentCallbacks
34
import android.content.Context
5+
import android.content.res.Configuration
46
import android.graphics.Bitmap
57
import android.os.Build
68
import android.view.MotionEvent
@@ -57,33 +59,36 @@ public class ReplayIntegration(
5759
private val context: Context,
5860
private val dateProvider: ICurrentDateProvider,
5961
private val recorderProvider: (() -> Recorder)? = null,
62+
private val recorderConfigProvider: ((configChanged: Boolean) -> ScreenshotRecorderConfig)? = null,
6063
private val replayCacheProvider: ((replayId: SentryId) -> ReplayCache)? = null
6164
) : Integration,
6265
Closeable,
6366
ScreenshotRecorderCallback,
6467
TouchRecorderCallback,
6568
ReplayController,
69+
ComponentCallbacks,
6670
IConnectionStatusObserver,
67-
IRateLimitObserver,
68-
WindowCallback {
71+
IRateLimitObserver {
6972

7073
// needed for the Java's call site
7174
constructor(context: Context, dateProvider: ICurrentDateProvider) : this(
7275
context.appContext(),
7376
dateProvider,
7477
null,
78+
null,
7579
null
7680
)
7781

7882
internal constructor(
7983
context: Context,
8084
dateProvider: ICurrentDateProvider,
8185
recorderProvider: (() -> Recorder)?,
86+
recorderConfigProvider: ((configChanged: Boolean) -> ScreenshotRecorderConfig)?,
8287
replayCacheProvider: ((replayId: SentryId) -> ReplayCache)?,
8388
replayCaptureStrategyProvider: ((isFullSession: Boolean) -> CaptureStrategy)? = null,
8489
mainLooperHandler: MainLooperHandler? = null,
8590
gestureRecorderProvider: (() -> GestureRecorder)? = null
86-
) : this(context.appContext(), dateProvider, recorderProvider, replayCacheProvider) {
91+
) : this(context.appContext(), dateProvider, recorderProvider, recorderConfigProvider, replayCacheProvider) {
8792
this.replayCaptureStrategyProvider = replayCaptureStrategyProvider
8893
this.mainLooperHandler = mainLooperHandler ?: MainLooperHandler()
8994
this.gestureRecorderProvider = gestureRecorderProvider
@@ -125,12 +130,23 @@ public class ReplayIntegration(
125130
}
126131

127132
this.hub = hub
128-
recorder = recorderProvider?.invoke() ?: WindowRecorder(options, this, this, mainLooperHandler, replayExecutor)
133+
recorder = recorderProvider?.invoke() ?: WindowRecorder(options, this, mainLooperHandler, replayExecutor)
129134
gestureRecorder = gestureRecorderProvider?.invoke() ?: GestureRecorder(options, this)
130135
isEnabled.set(true)
131136

132137
options.connectionStatusProvider.addConnectionStatusObserver(this)
133138
hub.rateLimiter?.addRateLimitObserver(this)
139+
if (options.sessionReplay.isTrackOrientationChange) {
140+
try {
141+
context.registerComponentCallbacks(this)
142+
} catch (e: Throwable) {
143+
options.logger.log(
144+
INFO,
145+
"ComponentCallbacks is not available, orientation changes won't be handled by Session replay",
146+
e
147+
)
148+
}
149+
}
134150

135151
addIntegrationToSdkVersion("Replay")
136152
SentryIntegrationPackageStorage.getInstance()
@@ -161,16 +177,17 @@ public class ReplayIntegration(
161177
return
162178
}
163179

164-
lifecycle.currentState = STARTED
180+
val recorderConfig = recorderConfigProvider?.invoke(false) ?: ScreenshotRecorderConfig.from(context, options.sessionReplay)
165181
captureStrategy = replayCaptureStrategyProvider?.invoke(isFullSession) ?: if (isFullSession) {
166182
SessionCaptureStrategy(options, hub, dateProvider, replayExecutor, replayCacheProvider)
167183
} else {
168184
BufferCaptureStrategy(options, hub, dateProvider, random, replayExecutor, replayCacheProvider)
169185
}
170-
recorder?.start()
171-
captureStrategy?.start()
172186

187+
captureStrategy?.start(recorderConfig)
188+
recorder?.start(recorderConfig)
173189
registerRootViewListeners()
190+
lifecycle.currentState = STARTED
174191
}
175192

176193
override fun resume() {
@@ -191,9 +208,9 @@ public class ReplayIntegration(
191208
return
192209
}
193210

194-
lifecycle.currentState = RESUMED
195211
captureStrategy?.resume()
196212
recorder?.resume()
213+
lifecycle.currentState = RESUMED
197214
}
198215

199216
@Synchronized
@@ -245,7 +262,6 @@ public class ReplayIntegration(
245262
}
246263

247264
unregisterRootViewListeners()
248-
recorder?.reset()
249265
recorder?.stop()
250266
gestureRecorder?.stop()
251267
captureStrategy?.stop()
@@ -277,7 +293,12 @@ public class ReplayIntegration(
277293

278294
options.connectionStatusProvider.removeConnectionStatusObserver(this)
279295
hub?.rateLimiter?.removeRateLimitObserver(this)
280-
296+
if (options.sessionReplay.isTrackOrientationChange) {
297+
try {
298+
context.unregisterComponentCallbacks(this)
299+
} catch (ignored: Throwable) {
300+
}
301+
}
281302
stop()
282303
recorder?.close()
283304
recorder = null
@@ -286,6 +307,24 @@ public class ReplayIntegration(
286307
lifecycle.currentState = CLOSED
287308
}
288309

310+
override fun onConfigurationChanged(newConfig: Configuration) {
311+
if (!isEnabled.get() || !isRecording()) {
312+
return
313+
}
314+
315+
recorder?.stop()
316+
317+
// refresh config based on new device configuration
318+
val recorderConfig = recorderConfigProvider?.invoke(true) ?: ScreenshotRecorderConfig.from(context, options.sessionReplay)
319+
captureStrategy?.onConfigurationChanged(recorderConfig)
320+
321+
recorder?.start(recorderConfig)
322+
// we have to restart recorder with a new config and pause immediately if the replay is paused
323+
if (lifecycle.currentState == PAUSED) {
324+
recorder?.pause()
325+
}
326+
}
327+
289328
override fun onConnectionStatusChanged(status: ConnectionStatus) {
290329
if (captureStrategy !is SessionCaptureStrategy) {
291330
// we only want to stop recording when offline for session mode
@@ -313,6 +352,8 @@ public class ReplayIntegration(
313352
}
314353
}
315354

355+
override fun onLowMemory() = Unit
356+
316357
override fun onTouchEvent(event: MotionEvent) {
317358
if (!isEnabled.get() || !lifecycle.isTouchRecordingAllowed()) {
318359
return
@@ -413,30 +454,6 @@ public class ReplayIntegration(
413454
}
414455
}
415456

416-
override fun onWindowSizeChanged(width: Int, height: Int) {
417-
if (!isEnabled.get() || !isRecording()) {
418-
return
419-
}
420-
if (options.sessionReplay.isTrackConfiguration) {
421-
val recorderConfig =
422-
ScreenshotRecorderConfig.fromSize(context, options.sessionReplay, width, height)
423-
onConfigurationChanged(recorderConfig)
424-
}
425-
}
426-
427-
public fun onConfigurationChanged(config: ScreenshotRecorderConfig) {
428-
if (!isEnabled.get() || !isRecording()) {
429-
return
430-
}
431-
captureStrategy?.onConfigurationChanged(config)
432-
recorder?.onConfigurationChanged(config)
433-
434-
// we have to restart recorder with a new config and pause immediately if the replay is paused
435-
if (lifecycle.currentState == PAUSED) {
436-
recorder?.pause()
437-
}
438-
}
439-
440457
private class PreviousReplayHint : Backfillable {
441458
override fun shouldEnrich(): Boolean = false
442459
}

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

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,15 @@ import android.graphics.Canvas
77
import android.graphics.Color
88
import android.graphics.Matrix
99
import android.graphics.Paint
10+
import android.graphics.Point
1011
import android.graphics.Rect
1112
import android.graphics.RectF
13+
import android.os.Build.VERSION
14+
import android.os.Build.VERSION_CODES
1215
import android.view.PixelCopy
1316
import android.view.View
1417
import android.view.ViewTreeObserver
18+
import android.view.WindowManager
1519
import io.sentry.SentryLevel.DEBUG
1620
import io.sentry.SentryLevel.INFO
1721
import io.sentry.SentryLevel.WARNING
@@ -174,9 +178,6 @@ internal class ScreenshotRecorder(
174178
}
175179

176180
override fun onDraw() {
177-
if (!isCapturing.get()) {
178-
return
179-
}
180181
val root = rootView?.get()
181182
if (root == null || root.width <= 0 || root.height <= 0 || !root.isShown) {
182183
options.logger.log(DEBUG, "Root view is invalid, not capturing screenshot")
@@ -289,26 +290,35 @@ public data class ScreenshotRecorderConfig(
289290
}
290291
}
291292

292-
fun fromSize(
293+
fun from(
293294
context: Context,
294-
sessionReplay: SentryReplayOptions,
295-
windowWidth: Int,
296-
windowHeight: Int
295+
sessionReplay: SentryReplayOptions
297296
): ScreenshotRecorderConfig {
297+
// PixelCopy takes screenshots including system bars, so we have to get the real size here
298+
val wm = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
299+
val screenBounds = if (VERSION.SDK_INT >= VERSION_CODES.R) {
300+
wm.currentWindowMetrics.bounds
301+
} else {
302+
val screenBounds = Point()
303+
@Suppress("DEPRECATION")
304+
wm.defaultDisplay.getRealSize(screenBounds)
305+
Rect(0, 0, screenBounds.x, screenBounds.y)
306+
}
307+
298308
// use the baseline density of 1x (mdpi)
299309
val (height, width) =
300-
((windowHeight / context.resources.displayMetrics.density) * sessionReplay.quality.sizeScale)
310+
((screenBounds.height() / context.resources.displayMetrics.density) * sessionReplay.quality.sizeScale)
301311
.roundToInt()
302312
.adjustToBlockSize() to
303-
((windowWidth / context.resources.displayMetrics.density) * sessionReplay.quality.sizeScale)
313+
((screenBounds.width() / context.resources.displayMetrics.density) * sessionReplay.quality.sizeScale)
304314
.roundToInt()
305315
.adjustToBlockSize()
306316

307317
return ScreenshotRecorderConfig(
308318
recordingWidth = width,
309319
recordingHeight = height,
310-
scaleFactorX = width.toFloat() / windowWidth,
311-
scaleFactorY = height.toFloat() / windowHeight,
320+
scaleFactorX = width.toFloat() / screenBounds.width(),
321+
scaleFactorY = height.toFloat() / screenBounds.height(),
312322
frameRate = sessionReplay.frameRate,
313323
bitRate = sessionReplay.quality.bitRate
314324
)
@@ -337,10 +347,3 @@ public interface ScreenshotRecorderCallback {
337347
*/
338348
fun onScreenshotRecorded(screenshot: File, frameTimestamp: Long)
339349
}
340-
341-
/**
342-
* A callback to be invoked when once current window size is determined or changes
343-
*/
344-
public interface WindowCallback {
345-
public fun onWindowSizeChanged(width: Int, height: Int)
346-
}

0 commit comments

Comments
 (0)