Skip to content

Commit 3ff77f9

Browse files
committed
WIP
1 parent b67c33c commit 3ff77f9

File tree

6 files changed

+33
-32
lines changed

6 files changed

+33
-32
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,6 @@ interface Recorder : Closeable {
1515
fun pause()
1616

1717
fun stop()
18+
19+
fun setReplayStrategy(isSession: Boolean) = Unit
1820
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ public class ReplayIntegration(
109109
}
110110

111111
this.hub = hub
112-
recorder = recorderProvider?.invoke() ?: WindowRecorder(options, this, mainLooperHandler)
112+
recorder = recorderProvider?.invoke() ?: WindowRecorder(options, this, mainLooperHandler, hub.rateLimiter)
113113
gestureRecorder = gestureRecorderProvider?.invoke() ?: GestureRecorder(options, this)
114114
isEnabled.set(true)
115115

@@ -157,6 +157,10 @@ public class ReplayIntegration(
157157

158158
captureStrategy?.start(recorderConfig)
159159
recorder?.start(recorderConfig)
160+
// TODO: move this here
161+
// TODO: add 2 listeners here: OnRateLimit(category, applied) and OnConnectionChanged(connected)
162+
// TODO: when they are fired, call either recorder?.resume() + strategy.resume() or recorder?.pause() + strategy.pause()
163+
recorder?.setReplayStrategy(captureStrategy is SessionCaptureStrategy)
160164
registerRootViewListeners()
161165
}
162166

@@ -184,6 +188,7 @@ public class ReplayIntegration(
184188
captureStrategy?.segmentTimestamp = newTimestamp
185189
})
186190
captureStrategy = captureStrategy?.convert()
191+
recorder?.setReplayStrategy(captureStrategy is SessionCaptureStrategy)
187192
}
188193

189194
override fun getReplayId(): SentryId = captureStrategy?.currentReplayId ?: SentryId.EMPTY_ID

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

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ import android.view.PixelCopy
1717
import android.view.View
1818
import android.view.ViewTreeObserver
1919
import android.view.WindowManager
20+
import io.sentry.DataCategory.All
21+
import io.sentry.DataCategory.Replay
22+
import io.sentry.IConnectionStatusProvider.ConnectionStatus.DISCONNECTED
2023
import io.sentry.SentryLevel.DEBUG
2124
import io.sentry.SentryLevel.INFO
2225
import io.sentry.SentryLevel.WARNING
@@ -30,12 +33,12 @@ import io.sentry.android.replay.util.traverse
3033
import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode
3134
import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.ImageViewHierarchyNode
3235
import io.sentry.android.replay.viewhierarchy.ViewHierarchyNode.TextViewHierarchyNode
36+
import io.sentry.transport.RateLimiter
3337
import java.io.File
3438
import java.lang.ref.WeakReference
3539
import java.util.concurrent.Executors
3640
import java.util.concurrent.ThreadFactory
3741
import java.util.concurrent.atomic.AtomicBoolean
38-
import java.util.concurrent.atomic.AtomicReference
3942
import kotlin.LazyThreadSafetyMode.NONE
4043
import kotlin.math.roundToInt
4144

@@ -44,14 +47,14 @@ internal class ScreenshotRecorder(
4447
val config: ScreenshotRecorderConfig,
4548
val options: SentryOptions,
4649
val mainLooperHandler: MainLooperHandler,
47-
private val screenshotRecorderCallback: ScreenshotRecorderCallback?
50+
private val screenshotRecorderCallback: ScreenshotRecorderCallback?,
51+
private val rateLimiter: RateLimiter?
4852
) : ViewTreeObserver.OnDrawListener {
4953

5054
private val recorder by lazy {
5155
Executors.newSingleThreadScheduledExecutor(RecorderExecutorServiceThreadFactory())
5256
}
5357
private var rootView: WeakReference<View>? = null
54-
private val pendingViewHierarchy = AtomicReference<ViewHierarchyNode>()
5558
private val maskingPaint by lazy(NONE) { Paint() }
5659
private val singlePixelBitmap: Bitmap by lazy(NONE) {
5760
Bitmap.createBitmap(
@@ -69,13 +72,24 @@ internal class ScreenshotRecorder(
6972
private val contentChanged = AtomicBoolean(false)
7073
private val isCapturing = AtomicBoolean(true)
7174
private var lastScreenshot: Bitmap? = null
75+
internal val isSession = AtomicBoolean(false)
7276

7377
fun capture() {
7478
if (!isCapturing.get()) {
7579
options.logger.log(DEBUG, "ScreenshotRecorder is paused, not capturing screenshot")
7680
return
7781
}
7882

83+
if (isSession.get() && options.connectionStatusProvider.connectionStatus == DISCONNECTED) {
84+
options.logger.log(DEBUG, "Skipping screenshot recording, no internet connection")
85+
return
86+
}
87+
88+
if (isSession.get() && (rateLimiter?.isActiveForCategory(All) == true || rateLimiter?.isActiveForCategory(Replay) == true)) {
89+
options.logger.log(DEBUG, "Skipping screenshot recording, rate limit is active")
90+
return
91+
}
92+
7993
if (!contentChanged.get() && lastScreenshot != null && !lastScreenshot!!.isRecycled) {
8094
options.logger.log(DEBUG, "Content hasn't changed, repeating last known frame")
8195

@@ -230,7 +244,6 @@ internal class ScreenshotRecorder(
230244
unbind(rootView?.get())
231245
rootView?.clear()
232246
lastScreenshot?.recycle()
233-
pendingViewHierarchy.set(null)
234247
isCapturing.set(false)
235248
recorder.gracefullyShutdown(options)
236249
}

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import io.sentry.SentryOptions
66
import io.sentry.android.replay.util.MainLooperHandler
77
import io.sentry.android.replay.util.gracefullyShutdown
88
import io.sentry.android.replay.util.scheduleAtFixedRateSafely
9+
import io.sentry.transport.RateLimiter
910
import java.lang.ref.WeakReference
1011
import java.util.concurrent.Executors
1112
import java.util.concurrent.ScheduledFuture
@@ -17,7 +18,8 @@ import java.util.concurrent.atomic.AtomicBoolean
1718
internal class WindowRecorder(
1819
private val options: SentryOptions,
1920
private val screenshotRecorderCallback: ScreenshotRecorderCallback? = null,
20-
private val mainLooperHandler: MainLooperHandler
21+
private val mainLooperHandler: MainLooperHandler,
22+
private val rateLimiter: RateLimiter?
2123
) : Recorder, OnRootViewsChangedListener {
2224

2325
internal companion object {
@@ -52,7 +54,7 @@ internal class WindowRecorder(
5254
return
5355
}
5456

55-
recorder = ScreenshotRecorder(recorderConfig, options, mainLooperHandler, screenshotRecorderCallback)
57+
recorder = ScreenshotRecorder(recorderConfig, options, mainLooperHandler, screenshotRecorderCallback, rateLimiter)
5658
capturingTask = capturer.scheduleAtFixedRateSafely(
5759
options,
5860
"$TAG.capture",
@@ -86,6 +88,10 @@ internal class WindowRecorder(
8688
capturer.gracefullyShutdown(options)
8789
}
8890

91+
override fun setReplayStrategy(isSession: Boolean) {
92+
recorder?.isSession?.set(isSession)
93+
}
94+
8995
private class RecorderExecutorServiceThreadFactory : ThreadFactory {
9096
private var cnt = 0
9197
override fun newThread(r: Runnable): Thread {

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

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -75,17 +75,6 @@ internal class SessionCaptureStrategy(
7575
}
7676

7777
override fun onScreenshotRecorded(bitmap: Bitmap?, store: ReplayCache.(frameTimestamp: Long) -> Unit) {
78-
if (options.connectionStatusProvider.connectionStatus == DISCONNECTED) {
79-
options.logger.log(DEBUG, "Skipping screenshot recording, no internet connection")
80-
bitmap?.recycle()
81-
return
82-
}
83-
val rateLimiter = hub?.rateLimiter
84-
if (rateLimiter?.isActiveForCategory(All) == true || rateLimiter?.isActiveForCategory(Replay) == true) {
85-
options.logger.log(DEBUG, "Skipping screenshot recording, rate limit is active")
86-
bitmap?.recycle()
87-
return
88-
}
8978
// have to do it before submitting, otherwise if the queue is busy, the timestamp won't be
9079
// reflecting the exact time of when it was captured
9180
val frameTimestamp = dateProvider.currentTimeMillis

sentry-android-replay/src/test/java/io/sentry/android/replay/capture/SessionCaptureStrategyTest.kt

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -372,18 +372,4 @@ class SessionCaptureStrategyTest {
372372
"the current replay cache folder is not being deleted."
373373
)
374374
}
375-
376-
@Test
377-
fun `when rate limited does not capture screenshots`() {
378-
val strategy = fixture.getSut()
379-
380-
whenever(fixture.hub.rateLimiter?.isActiveForCategory(eq(DataCategory.Replay))).thenReturn(true)
381-
382-
strategy.start(fixture.recorderConfig)
383-
var called = false
384-
strategy.onScreenshotRecorded(mock<Bitmap>()) {
385-
called = true
386-
}
387-
assertFalse(called)
388-
}
389375
}

0 commit comments

Comments
 (0)