@@ -60,13 +60,25 @@ internal class ScreenshotRecorder(
6060 private val debugOverlayDrawable = DebugOverlayDrawable ()
6161
6262 fun capture () {
63+ if (options.sessionReplay.isDebug) {
64+ options.logger.log(DEBUG , " Capturing screenshot, isCapturing: %s" , isCapturing.get())
65+ }
6366 if (! isCapturing.get()) {
6467 if (options.sessionReplay.isDebug) {
6568 options.logger.log(DEBUG , " ScreenshotRecorder is paused, not capturing screenshot" )
6669 }
6770 return
6871 }
6972
73+ if (options.sessionReplay.isDebug) {
74+ options.logger.log(
75+ DEBUG ,
76+ " Capturing screenshot, contentChanged: %s, lastCaptureSuccessful: %s" ,
77+ contentChanged.get(),
78+ lastCaptureSuccessful.get(),
79+ )
80+ }
81+
7082 if (! contentChanged.get() && lastCaptureSuccessful.get()) {
7183 screenshotRecorderCallback?.onScreenshotRecorded(screenshot)
7284 return
@@ -84,99 +96,95 @@ internal class ScreenshotRecorder(
8496 return
8597 }
8698
87- // postAtFrontOfQueue to ensure the view hierarchy and bitmap are ase close in-sync as possible
88- mainLooperHandler.post {
89- try {
90- contentChanged.set(false )
91- PixelCopy .request(
92- window,
93- screenshot,
94- { copyResult: Int ->
95- if (copyResult != PixelCopy .SUCCESS ) {
96- options.logger.log(INFO , " Failed to capture replay recording: %d" , copyResult)
97- lastCaptureSuccessful.set(false )
98- return @request
99- }
100-
101- // TODO: handle animations with heuristics (e.g. if we fall under this condition 2 times
102- // in a row, we should capture)
103- if (contentChanged.get()) {
104- options.logger.log(INFO , " Failed to determine view hierarchy, not capturing" )
105- lastCaptureSuccessful.set(false )
106- return @request
107- }
99+ try {
100+ contentChanged.set(false )
101+ PixelCopy .request(
102+ window,
103+ screenshot,
104+ { copyResult: Int ->
105+ if (copyResult != PixelCopy .SUCCESS ) {
106+ options.logger.log(INFO , " Failed to capture replay recording: %d" , copyResult)
107+ lastCaptureSuccessful.set(false )
108+ return @request
109+ }
110+
111+ // TODO: handle animations with heuristics (e.g. if we fall under this condition 2 times
112+ // in a row, we should capture)
113+ if (contentChanged.get()) {
114+ options.logger.log(INFO , " Failed to determine view hierarchy, not capturing" )
115+ lastCaptureSuccessful.set(false )
116+ return @request
117+ }
118+
119+ // TODO: disableAllMasking here and dont traverse?
120+ val viewHierarchy = ViewHierarchyNode .fromView(root, null , 0 , options)
121+ root.traverse(viewHierarchy, options)
122+
123+ recorder.submitSafely(options, " screenshot_recorder.mask" ) {
124+ val debugMasks = mutableListOf<Rect >()
125+
126+ val canvas = Canvas (screenshot)
127+ canvas.setMatrix(prescaledMatrix)
128+ viewHierarchy.traverse { node ->
129+ if (node.shouldMask && (node.width > 0 && node.height > 0 )) {
130+ node.visibleRect ? : return @traverse false
131+
132+ // TODO: investigate why it returns true on RN when it shouldn't
133+ // if (viewHierarchy.isObscured(node)) {
134+ // return@traverse true
135+ // }
136+
137+ val (visibleRects, color) =
138+ when (node) {
139+ is ImageViewHierarchyNode -> {
140+ listOf (node.visibleRect) to screenshot.dominantColorForRect(node.visibleRect)
141+ }
108142
109- // TODO: disableAllMasking here and dont traverse?
110- val viewHierarchy = ViewHierarchyNode .fromView(root, null , 0 , options)
111- root.traverse(viewHierarchy, options)
112-
113- recorder.submitSafely(options, " screenshot_recorder.mask" ) {
114- val debugMasks = mutableListOf<Rect >()
115-
116- val canvas = Canvas (screenshot)
117- canvas.setMatrix(prescaledMatrix)
118- viewHierarchy.traverse { node ->
119- if (node.shouldMask && (node.width > 0 && node.height > 0 )) {
120- node.visibleRect ? : return @traverse false
121-
122- // TODO: investigate why it returns true on RN when it shouldn't
123- // if (viewHierarchy.isObscured(node)) {
124- // return@traverse true
125- // }
126-
127- val (visibleRects, color) =
128- when (node) {
129- is ImageViewHierarchyNode -> {
130- listOf (node.visibleRect) to
131- screenshot.dominantColorForRect(node.visibleRect)
132- }
133-
134- is TextViewHierarchyNode -> {
135- val textColor =
136- node.layout?.dominantTextColor ? : node.dominantColor ? : Color .BLACK
137- node.layout.getVisibleRects(
138- node.visibleRect,
139- node.paddingLeft,
140- node.paddingTop,
141- ) to textColor
142- }
143-
144- else -> {
145- listOf (node.visibleRect) to Color .BLACK
146- }
143+ is TextViewHierarchyNode -> {
144+ val textColor =
145+ node.layout?.dominantTextColor ? : node.dominantColor ? : Color .BLACK
146+ node.layout.getVisibleRects(
147+ node.visibleRect,
148+ node.paddingLeft,
149+ node.paddingTop,
150+ ) to textColor
147151 }
148152
149- maskingPaint.setColor(color)
150- visibleRects.forEach { rect ->
151- canvas.drawRoundRect(RectF (rect), 10f , 10f , maskingPaint)
152- }
153- if (options.replayController.isDebugMaskingOverlayEnabled()) {
154- debugMasks.addAll(visibleRects)
153+ else -> {
154+ listOf (node.visibleRect) to Color .BLACK
155+ }
155156 }
157+
158+ maskingPaint.setColor(color)
159+ visibleRects.forEach { rect ->
160+ canvas.drawRoundRect(RectF (rect), 10f , 10f , maskingPaint)
161+ }
162+ if (options.replayController.isDebugMaskingOverlayEnabled()) {
163+ debugMasks.addAll(visibleRects)
156164 }
157- return @traverse true
158165 }
166+ return @traverse true
167+ }
159168
160- if (options.replayController.isDebugMaskingOverlayEnabled()) {
161- mainLooperHandler.post {
162- if (debugOverlayDrawable.callback == null ) {
163- root.overlay.add(debugOverlayDrawable)
164- }
165- debugOverlayDrawable.updateMasks(debugMasks)
166- root.postInvalidate()
169+ if (options.replayController.isDebugMaskingOverlayEnabled()) {
170+ mainLooperHandler.post {
171+ if (debugOverlayDrawable.callback == null ) {
172+ root.overlay.add(debugOverlayDrawable)
167173 }
174+ debugOverlayDrawable.updateMasks(debugMasks)
175+ root.postInvalidate()
168176 }
169- screenshotRecorderCallback?.onScreenshotRecorded(screenshot)
170- lastCaptureSuccessful.set(true )
171- contentChanged.set(false )
172177 }
173- },
174- mainLooperHandler.handler,
175- )
176- } catch (e: Throwable ) {
177- options.logger.log(WARNING , " Failed to capture replay recording" , e)
178- lastCaptureSuccessful.set(false )
179- }
178+ screenshotRecorderCallback?.onScreenshotRecorded(screenshot)
179+ lastCaptureSuccessful.set(true )
180+ contentChanged.set(false )
181+ }
182+ },
183+ mainLooperHandler.handler,
184+ )
185+ } catch (e: Throwable ) {
186+ options.logger.log(WARNING , " Failed to capture replay recording" , e)
187+ lastCaptureSuccessful.set(false )
180188 }
181189 }
182190
0 commit comments