Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion sentry-android-replay/api/sentry-android-replay.api
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,11 @@ public final class io/sentry/android/replay/ModifierExtensionsKt {
}

public abstract interface class io/sentry/android/replay/Recorder : java/io/Closeable {
public abstract fun onConfigurationChanged (Lio/sentry/android/replay/ScreenshotRecorderConfig;)V
public abstract fun pause ()V
public abstract fun reset ()V
public abstract fun resume ()V
public abstract fun start (Lio/sentry/android/replay/ScreenshotRecorderConfig;)V
public abstract fun start ()V
public abstract fun stop ()V
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ public interface Recorder : Closeable {
* at which the screenshots should be taken, and the screenshots size/resolution, which can
* change e.g. in the case of orientation change or window size change
*/
public fun start(recorderConfig: ScreenshotRecorderConfig)
public fun start()

public fun onConfigurationChanged(config: ScreenshotRecorderConfig)

public fun resume()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,9 @@ public class ReplayIntegration(
} else {
BufferCaptureStrategy(options, scopes, dateProvider, random, replayExecutor, replayCacheProvider)
}
recorder?.start()
captureStrategy?.start()

registerRootViewListeners()
}
}
Expand Down Expand Up @@ -331,9 +334,6 @@ public class ReplayIntegration(
}

recorder?.stop()

// once the window size is determined
// onWindowSizeChanged is triggered and we'll start the actual capturing
}

override fun onConnectionStatusChanged(status: ConnectionStatus) {
Expand Down Expand Up @@ -470,18 +470,9 @@ public class ReplayIntegration(
return
}

recorder?.stop()

val recorderConfig = recorderConfigProvider?.invoke(true) ?: ScreenshotRecorderConfig.fromSize(context, options.sessionReplay, width, height)

captureStrategy?.let { capture ->
if (capture.currentReplayId == SentryId.EMPTY_ID) {
capture.start(recorderConfig)
} else {
capture.onConfigurationChanged(recorderConfig)
}
}
recorder?.start(recorderConfig)
captureStrategy?.onConfigurationChanged(recorderConfig)
recorder?.onConfigurationChanged(recorderConfig)

// we have to restart recorder with a new config and pause immediately if the replay is paused
if (lifecycle.currentState == PAUSED) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,12 @@ internal class WindowRecorder(
}
}

override fun start(recorderConfig: ScreenshotRecorderConfig) {
if (isRecording.getAndSet(true)) {
override fun start() {
isRecording.getAndSet(true)
}

override fun onConfigurationChanged(recorderConfig: ScreenshotRecorderConfig) {
if (!isRecording.get()) {
Comment thread
markushi marked this conversation as resolved.
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ internal abstract class BaseCaptureStrategy(

protected val isTerminating = AtomicBoolean(false)
protected var cache: ReplayCache? = null
protected var recorderConfig: ScreenshotRecorderConfig by persistableAtomic { _, _, newValue ->
protected var recorderConfig: ScreenshotRecorderConfig? by persistableAtomic { _, _, newValue ->
Comment thread
markushi marked this conversation as resolved.
Outdated
if (newValue == null) {
// recorderConfig is only nullable on init, but never after
return@persistableAtomic
Expand All @@ -85,7 +85,6 @@ internal abstract class BaseCaptureStrategy(
protected val currentEvents: Deque<RRWebEvent> = ConcurrentLinkedDeque()

override fun start(
recorderConfig: ScreenshotRecorderConfig,
segmentId: Int,
replayId: SentryId,
replayType: ReplayType?
Expand All @@ -95,7 +94,6 @@ internal abstract class BaseCaptureStrategy(
this.currentReplayId = replayId
this.currentSegment = segmentId
this.replayType = replayType ?: (if (this is SessionCaptureStrategy) SESSION else BUFFER)
this.recorderConfig = recorderConfig

segmentTimestamp = DateUtils.getCurrentDateTime()
replayStartTimestamp.set(dateProvider.currentTimeMillis)
Expand All @@ -122,10 +120,10 @@ internal abstract class BaseCaptureStrategy(
segmentId: Int,
height: Int,
width: Int,
frameRate: Int,
bitRate: Int,
replayType: ReplayType = this.replayType,
cache: ReplayCache? = this.cache,
frameRate: Int = recorderConfig.frameRate,
bitRate: Int = recorderConfig.bitRate,
screenAtStart: String? = this.screenAtStart,
breadcrumbs: List<Breadcrumb>? = null,
events: Deque<RRWebEvent> = this.currentEvents
Expand Down Expand Up @@ -153,9 +151,11 @@ internal abstract class BaseCaptureStrategy(
}

override fun onTouchEvent(event: MotionEvent) {
val rrwebEvents = gestureConverter.convert(event, recorderConfig)
if (rrwebEvents != null) {
currentEvents += rrwebEvents
recorderConfig?.let { config ->
val rrwebEvents = gestureConverter.convert(event, config)
if (rrwebEvents != null) {
currentEvents += rrwebEvents
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ internal class BufferCaptureStrategy(
}
// we hand over replayExecutor to the new strategy to preserve order of execution
val captureStrategy = SessionCaptureStrategy(options, scopes, dateProvider, replayExecutor)
captureStrategy.start(recorderConfig, segmentId = currentSegment, replayId = currentReplayId, replayType = BUFFER)
captureStrategy.start(segmentId = currentSegment, replayId = currentReplayId, replayType = BUFFER)
return captureStrategy
}

Expand Down Expand Up @@ -189,24 +189,33 @@ internal class BufferCaptureStrategy(
}

private fun createCurrentSegment(taskName: String, onSegmentCreated: (ReplaySegment) -> Unit) {
val errorReplayDuration = options.sessionReplay.errorReplayDuration
val now = dateProvider.currentTimeMillis
val currentSegmentTimestamp = if (cache?.frames?.isNotEmpty() == true) {
// in buffer mode we have to set the timestamp of the first frame as the actual start
DateUtils.getDateTime(cache!!.frames.first().timestamp)
} else {
DateUtils.getDateTime(now - errorReplayDuration)
}
val segmentId = currentSegment
val duration = now - currentSegmentTimestamp.time
val replayId = currentReplayId
val height = this.recorderConfig.recordingHeight
val width = this.recorderConfig.recordingWidth

replayExecutor.submitSafely(options, "$TAG.$taskName") {
val segment =
createSegmentInternal(duration, currentSegmentTimestamp, replayId, segmentId, height, width)
onSegmentCreated(segment)
recorderConfig?.let { config ->
val errorReplayDuration = options.sessionReplay.errorReplayDuration
val now = dateProvider.currentTimeMillis
val currentSegmentTimestamp = if (cache?.frames?.isNotEmpty() == true) {
// in buffer mode we have to set the timestamp of the first frame as the actual start
DateUtils.getDateTime(cache!!.frames.first().timestamp)
} else {
DateUtils.getDateTime(now - errorReplayDuration)
}
val segmentId = currentSegment
val duration = now - currentSegmentTimestamp.time
val replayId = currentReplayId

replayExecutor.submitSafely(options, "$TAG.$taskName") {
val segment =
createSegmentInternal(
duration,
currentSegmentTimestamp,
replayId,
segmentId,
config.recordingHeight,
config.recordingWidth,
config.frameRate,
config.bitRate
)
onSegmentCreated(segment)
}
Comment thread
markushi marked this conversation as resolved.
Outdated
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ internal interface CaptureStrategy {
var segmentTimestamp: Date?

fun start(
recorderConfig: ScreenshotRecorderConfig,
segmentId: Int = 0,
replayId: SentryId = SentryId(),
replayType: ReplayType? = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ internal class SessionCaptureStrategy(
}

override fun start(
recorderConfig: ScreenshotRecorderConfig,
segmentId: Int,
replayId: SentryId,
replayType: ReplayType?
) {
super.start(recorderConfig, segmentId, replayId, replayType)
super.start(segmentId, replayId, replayType)
// only set replayId on the scope if it's a full session, otherwise all events will be
// tagged with the replay that might never be sent when we're recording in buffer mode
scopes?.configureScope {
Expand Down Expand Up @@ -80,8 +79,6 @@ internal class SessionCaptureStrategy(
// have to do it before submitting, otherwise if the queue is busy, the timestamp won't be
// reflecting the exact time of when it was captured
val frameTimestamp = dateProvider.currentTimeMillis
val height = recorderConfig.recordingHeight
val width = recorderConfig.recordingWidth
replayExecutor.submitSafely(options, "$TAG.add_frame") {
cache?.store(frameTimestamp)

Expand All @@ -98,20 +95,24 @@ internal class SessionCaptureStrategy(

val now = dateProvider.currentTimeMillis
if ((now - currentSegmentTimestamp.time >= options.sessionReplay.sessionSegmentDuration)) {
val segment =
createSegmentInternal(
options.sessionReplay.sessionSegmentDuration,
currentSegmentTimestamp,
currentReplayId,
currentSegment,
height,
width
)
if (segment is ReplaySegment.Created) {
segment.capture(scopes)
currentSegment++
// set next segment timestamp as close to the previous one as possible to avoid gaps
segmentTimestamp = segment.replay.timestamp
recorderConfig?.let { config ->
Comment thread
markushi marked this conversation as resolved.
Outdated
val segment =
createSegmentInternal(
options.sessionReplay.sessionSegmentDuration,
currentSegmentTimestamp,
currentReplayId,
currentSegment,
config.recordingHeight,
config.recordingWidth,
config.frameRate,
config.bitRate
)
if (segment is ReplaySegment.Created) {
segment.capture(scopes)
currentSegment++
// set next segment timestamp as close to the previous one as possible to avoid gaps
segmentTimestamp = segment.replay.timestamp
}
}
}

Expand Down Expand Up @@ -140,17 +141,26 @@ internal class SessionCaptureStrategy(
override fun convert(): CaptureStrategy = this

private fun createCurrentSegment(taskName: String, onSegmentCreated: (ReplaySegment) -> Unit) {
val now = dateProvider.currentTimeMillis
val currentSegmentTimestamp = segmentTimestamp ?: return
val segmentId = currentSegment
val duration = now - currentSegmentTimestamp.time
val replayId = currentReplayId
val height = recorderConfig.recordingHeight
val width = recorderConfig.recordingWidth
replayExecutor.submitSafely(options, "$TAG.$taskName") {
val segment =
createSegmentInternal(duration, currentSegmentTimestamp, replayId, segmentId, height, width)
onSegmentCreated(segment)
recorderConfig?.let { config ->
val now = dateProvider.currentTimeMillis
val currentSegmentTimestamp = segmentTimestamp ?: return
val segmentId = currentSegment
val duration = now - currentSegmentTimestamp.time
val replayId = currentReplayId
replayExecutor.submitSafely(options, "$TAG.$taskName") {
val segment =
createSegmentInternal(
duration,
currentSegmentTimestamp,
replayId,
segmentId,
config.recordingHeight,
config.recordingWidth,
config.frameRate,
config.bitRate
)
onSegmentCreated(segment)
}
Comment thread
markushi marked this conversation as resolved.
Outdated
}
}
}
Loading
Loading