Skip to content

Commit 0476132

Browse files
committed
Adhere to rrweb move event expectations
1 parent 234b789 commit 0476132

File tree

1 file changed

+41
-16
lines changed

1 file changed

+41
-16
lines changed

sentry-android-replay/src/main/java/io/sentry/android/replay/capture/BaseCaptureStrategy.kt

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import io.sentry.transport.ICurrentDateProvider
3030
import io.sentry.util.FileUtils
3131
import java.io.File
3232
import java.util.Date
33+
import java.util.concurrent.CopyOnWriteArrayList
3334
import java.util.concurrent.Executors
3435
import java.util.concurrent.ScheduledExecutorService
3536
import java.util.concurrent.ThreadFactory
@@ -48,7 +49,6 @@ internal abstract class BaseCaptureStrategy(
4849

4950
internal companion object {
5051
private const val TAG = "CaptureStrategy"
51-
private const val DEBOUNCE_TIMEOUT = 200
5252
private val snakecasePattern = "_[a-z]".toRegex()
5353
private val supportedNetworkData = setOf(
5454
"status_code",
@@ -58,6 +58,9 @@ internal abstract class BaseCaptureStrategy(
5858
"http.response_content_length",
5959
"http.request_content_length"
6060
)
61+
// rrweb values
62+
private const val TOUCH_MOVE_DEBOUNCE_THRESHOLD = 50
63+
private const val CAPTURE_MOVE_EVENT_THRESHOLD = 500
6164
}
6265

6366
protected var cache: ReplayCache? = null
@@ -66,8 +69,11 @@ internal abstract class BaseCaptureStrategy(
6669
override val currentReplayId = AtomicReference(SentryId.EMPTY_ID)
6770
override val currentSegment = AtomicInteger(0)
6871
override val replayCacheDir: File? get() = cache?.replayCacheDir
69-
private val currentEvents = mutableListOf<RRWebEvent>()
70-
private val lastExecutionTime = AtomicLong(0)
72+
73+
private val currentEvents = CopyOnWriteArrayList<RRWebEvent>()
74+
private val currentPositions = mutableListOf<Position>()
75+
private var touchMoveBaseline = 0L
76+
private var lastCapturedMoveEvent = 0L
7177

7278
protected val replayExecutor: ScheduledExecutorService by lazy {
7379
executor ?: Executors.newSingleThreadScheduledExecutor(ReplayExecutorServiceThreadFactory())
@@ -281,6 +287,7 @@ internal abstract class BaseCaptureStrategy(
281287
}
282288

283289
override fun onTouchEvent(event: MotionEvent) {
290+
// TODO: rotate in buffer mode
284291
val rrwebEvent = event.toRRWebIncrementalSnapshotEvent()
285292
if (rrwebEvent != null) {
286293
currentEvents += rrwebEvent
@@ -363,30 +370,47 @@ internal abstract class BaseCaptureStrategy(
363370
return when (val action = event.actionMasked) {
364371
MotionEvent.ACTION_MOVE -> {
365372
// we only throttle move events as those can be overwhelming
366-
val now = dateProvider.getCurrentTimeMillis()
367-
if (lastExecutionTime.get() != 0L && lastExecutionTime.get() + DEBOUNCE_TIMEOUT > now) {
373+
val now = dateProvider.currentTimeMillis
374+
if (lastCapturedMoveEvent != 0L && lastCapturedMoveEvent + TOUCH_MOVE_DEBOUNCE_THRESHOLD > now) {
368375
return null
369376
}
370-
lastExecutionTime.set(now)
377+
lastCapturedMoveEvent = now
371378

372-
RRWebInteractionMoveEvent().apply {
373-
timestamp = dateProvider.currentTimeMillis
374-
positions = listOf(
375-
Position().apply {
376-
x = event.x * recorderConfig.scaleFactorX
377-
y = event.y * recorderConfig.scaleFactorY
378-
id = event.getPointerId(event.actionIndex)
379-
timeOffset = 0 // TODO: is this needed?
379+
// idk why but rrweb does it like dis
380+
if (touchMoveBaseline == 0L) {
381+
touchMoveBaseline = now
382+
}
383+
384+
currentPositions += Position().apply {
385+
x = event.x * recorderConfig.scaleFactorX
386+
y = event.y * recorderConfig.scaleFactorY
387+
id = 0 // html node id, but we don't have it, so hardcode to 0 to align with FE
388+
timeOffset = now - touchMoveBaseline
389+
}
390+
391+
val totalOffset = now - touchMoveBaseline
392+
return if (totalOffset > CAPTURE_MOVE_EVENT_THRESHOLD) {
393+
RRWebInteractionMoveEvent().apply {
394+
timestamp = now
395+
positions = currentPositions.map { pos ->
396+
pos.timeOffset -= totalOffset
397+
pos
380398
}
381-
) // TODO: support multiple pointers
399+
}.also {
400+
currentPositions.clear()
401+
touchMoveBaseline = 0L
402+
}
403+
} else {
404+
null
382405
}
383406
}
407+
384408
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
385409
RRWebInteractionEvent().apply {
386410
timestamp = dateProvider.currentTimeMillis
387411
x = event.x * recorderConfig.scaleFactorX
388412
y = event.y * recorderConfig.scaleFactorY
389-
id = event.getPointerId(event.actionIndex)
413+
id = 0 // html node id, but we don't have it, so hardcode to 0 to align with FE
390414
interactionType = when (action) {
391415
MotionEvent.ACTION_UP -> InteractionType.TouchEnd
392416
MotionEvent.ACTION_DOWN -> InteractionType.TouchStart
@@ -395,6 +419,7 @@ internal abstract class BaseCaptureStrategy(
395419
}
396420
}
397421
}
422+
398423
else -> null
399424
}
400425
}

0 commit comments

Comments
 (0)