From 18ef75b3d8aed811ee4fece5bec7027faf24b0d1 Mon Sep 17 00:00:00 2001 From: Miguel Juarez Lopez Date: Thu, 7 Nov 2024 15:10:12 -0500 Subject: [PATCH 01/12] Pass 1: Get rid of replayDependencies --- platform/jvm/capture-apollo3/build.gradle.kts | 2 +- platform/jvm/capture/build.gradle.kts | 2 +- .../capture/events/SessionReplayTarget.kt | 2 +- .../bitdrift/capture/replay/ReplayModule.kt | 70 ++++++++++--------- .../capture/replay/ReplayPreviewClient.kt | 23 +++--- .../capture/replay/internal/ReplayCapture.kt | 13 ++-- .../internal/ReplayCaptureController.kt | 6 +- .../replay/internal/ReplayDecorations.kt | 11 ++- .../replay/internal/ReplayDependencies.kt | 22 +++--- .../capture/replay/internal/ReplayParser.kt | 15 ++-- .../internal/ViewMapperConfiguration.kt | 5 +- .../capture/replay/internal/WindowManager.kt | 4 +- .../internal/compose/ComposeTreeParser.kt | 6 +- .../replay/internal/mappers/ViewMapper.kt | 49 +++++++------ 14 files changed, 121 insertions(+), 109 deletions(-) diff --git a/platform/jvm/capture-apollo3/build.gradle.kts b/platform/jvm/capture-apollo3/build.gradle.kts index c40d2ff3..d724ac83 100644 --- a/platform/jvm/capture-apollo3/build.gradle.kts +++ b/platform/jvm/capture-apollo3/build.gradle.kts @@ -57,7 +57,7 @@ dependencies { implementation(project(":capture")) implementation("com.apollographql.apollo3:apollo-runtime:3.8.3") - testImplementation("com.google.truth:truth:1.1.4") + testImplementation("com.google.truth:truth:1.4.4") testImplementation("junit:junit:4.13.2") testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0") // last version with Java 8 support } diff --git a/platform/jvm/capture/build.gradle.kts b/platform/jvm/capture/build.gradle.kts index 239d4fcf..531b77b1 100644 --- a/platform/jvm/capture/build.gradle.kts +++ b/platform/jvm/capture/build.gradle.kts @@ -84,7 +84,7 @@ cargo { module = "../.." targetDirectory = "../../../target" targets = listOf("arm64", "x86_64") - pythonCommand = "python3" + pythonCommand = "/opt/homebrew/bin/python3.12" } // workaround bug in rust-android-gradle plugin that causes .so to not be available on app launch diff --git a/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt b/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt index 06cb8591..1c0c2cc3 100644 --- a/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt +++ b/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt @@ -60,7 +60,7 @@ internal class SessionReplayTarget( } override fun onScreenCaptured(encodedScreen: ByteArray, screen: FilteredCapture, metrics: EncodedScreenMetrics) { - val fields = buildMap { + val fields = buildMap { put("screen", encodedScreen.toFieldValue()) putAll(metrics.toMap().toFields()) } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt index 604435f6..c2f8a775 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt @@ -10,26 +10,29 @@ package io.bitdrift.capture.replay import android.content.Context import io.bitdrift.capture.common.ErrorHandler import io.bitdrift.capture.common.MainThreadHandler +import io.bitdrift.capture.replay.internal.DisplayManagers +import io.bitdrift.capture.replay.internal.ReplayCapture import io.bitdrift.capture.replay.internal.ReplayCaptureController -import io.bitdrift.capture.replay.internal.ReplayDependencies +//import io.bitdrift.capture.replay.internal.ReplayDependencies +// TODO(murki): [Replay] Re-enable internal diagnostic logging // This is the logger called from the replay module source code. -internal typealias L = ReplayModuleInternalLogs +//internal typealias L = ReplayModuleInternalLogs -internal object ReplayModuleInternalLogs { - - fun v(message: String) { - ReplayModule.replayDependencies.logger.logVerboseInternal(message) - } - - fun d(message: String) { - ReplayModule.replayDependencies.logger.logDebugInternal(message) - } - - fun e(e: Throwable?, message: String) { - ReplayModule.replayDependencies.logger.logErrorInternal(message, e) - } -} +//internal object ReplayModuleInternalLogs { +// +// fun v(message: String) { +// ReplayModule.replayDependencies.logger.logVerboseInternal(message) +// } +// +// fun d(message: String) { +// ReplayModule.replayDependencies.logger.logDebugInternal(message) +// } +// +// fun e(e: Throwable?, message: String) { +// ReplayModule.replayDependencies.logger.logErrorInternal(message, e) +// } +//} /** * Sets up and controls the replay feature @@ -39,25 +42,28 @@ internal object ReplayModuleInternalLogs { * @param runtime allows for the feature to be remotely disabled */ class ReplayModule( - errorHandler: ErrorHandler, + internal val errorHandler: ErrorHandler, internal val logger: ReplayLogger, - sessionReplayConfiguration: SessionReplayConfiguration, + internal val sessionReplayConfiguration: SessionReplayConfiguration, context: Context, mainThreadHandler: MainThreadHandler = MainThreadHandler(), ) { private var replayCaptureController: ReplayCaptureController + internal val displayManager: DisplayManagers = DisplayManagers() + + private val replayCapture: ReplayCapture by lazy { + ReplayCapture(sessionReplayConfiguration, errorHandler, displayManager) + } + init { - replayDependencies = ReplayDependencies( - errorHandler = errorHandler, - logger = logger, - sessionReplayConfiguration = sessionReplayConfiguration, - ) - replayDependencies.displayManager.init(context) - replayCaptureController = ReplayCaptureController( - logger = logger, - mainThreadHandler = mainThreadHandler, - ) +// replayDependencies = ReplayDependencies( +// errorHandler = errorHandler, +// logger = logger, +// sessionReplayConfiguration = sessionReplayConfiguration, +// ) + displayManager.init(context) + replayCaptureController = ReplayCaptureController(replayCapture, logger, mainThreadHandler) } /** @@ -68,8 +74,8 @@ class ReplayModule( replayCaptureController.captureScreen(skipReplayComposeViews) } - companion object { - // TODO(murki): Refactor dependencies to not rely on singleton god state - internal lateinit var replayDependencies: ReplayDependencies - } +// companion object { +// // TODO(murki): Refactor dependencies to not rely on singleton god state +// internal lateinit var replayDependencies: ReplayDependencies +// } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt index bb5d751b..5dbcc511 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt @@ -32,31 +32,26 @@ import java.util.concurrent.TimeUnit * @param port The port to connect to (default is 3001) */ class ReplayPreviewClient( - private val replayModule: ReplayModule, + private val replayModule: ReplayModule, // TODO(murki): [Replay] Refactor to not expose this protocol: String = "ws", host: String = "10.0.2.2", port: Int = 3001, ) : ReplayLogger { - private val replayCapture: ReplayCapture = ReplayCapture() + private val replayCapture: ReplayCapture = ReplayCapture(replayModule.sessionReplayConfiguration, replayModule.errorHandler, replayModule.displayManager) private val executor: ExecutorService = Executors.newSingleThreadExecutor { Thread(it, "io.bitdrift.capture.session-replay-client") } - private val client: OkHttpClient - private val request: Request + // Calling this is necessary to capture the display size + private val client: OkHttpClient = OkHttpClient.Builder() + .readTimeout(0, TimeUnit.MILLISECONDS) + .build() + private val request: Request = Request.Builder() + .url("$protocol://$host:$port") + .build() private var webSocket: WebSocket? = null private var lastEncodedScreen: ByteArray? = null - init { - // Calling this is necessary to capture the display size - client = OkHttpClient.Builder() - .readTimeout(0, TimeUnit.MILLISECONDS) - .build() - request = Request.Builder() - .url("$protocol://$host:$port") - .build() - } - /** * Creates a socket and establishes a connection. Terminates any previous connection */ diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt index 2d4c2ca5..56eb0e63 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt @@ -8,16 +8,21 @@ package io.bitdrift.capture.replay.internal import io.bitdrift.capture.common.DefaultClock +import io.bitdrift.capture.common.ErrorHandler import io.bitdrift.capture.common.IClock -import io.bitdrift.capture.replay.L +import io.bitdrift.capture.replay.SessionReplayConfiguration +//import io.bitdrift.capture.replay.L import java.util.concurrent.ExecutorService import kotlin.time.measureTimedValue // This is the main logic for capturing a screen internal class ReplayCapture( - private val captureParser: ReplayParser = ReplayParser(), + sessionReplayConfiguration: SessionReplayConfiguration, + errorHandler: ErrorHandler, + displayManager: DisplayManagers, + private val captureParser: ReplayParser = ReplayParser(sessionReplayConfiguration, errorHandler), private val captureFilter: ReplayFilter = ReplayFilter(), - private val captureDecorations: ReplayDecorations = ReplayDecorations(), + private val captureDecorations: ReplayDecorations = ReplayDecorations(errorHandler, displayManager), private val replayEncoder: ReplayEncoder = ReplayEncoder(), private val clock: IClock = DefaultClock.getInstance(), ) { @@ -41,7 +46,7 @@ internal class ReplayCapture( val screen = captureDecorations.addDecorations(filteredCapture) val encodedScreen = replayEncoder.encode(screen) encodedScreenMetrics.captureTimeMs = clock.elapsedRealtime() - startTime - L.d("Screen Captured: $encodedScreenMetrics") +// L.d("Screen Captured: $encodedScreenMetrics") completion(encodedScreen, screen, encodedScreenMetrics) } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureController.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureController.kt index b6b7f74c..a0822aee 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureController.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureController.kt @@ -10,17 +10,19 @@ package io.bitdrift.capture.replay.internal import io.bitdrift.capture.common.MainThreadHandler import io.bitdrift.capture.replay.ReplayLogger import io.bitdrift.capture.replay.ReplayModule +import io.bitdrift.capture.replay.SessionReplayConfiguration import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService // Captures wireframe and pixel perfect representations of app's screen. internal class ReplayCaptureController( + private val replayCapture: ReplayCapture, + private val logger: ReplayLogger, private val mainThreadHandler: MainThreadHandler, private val executor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor { Thread(it, "io.bitdrift.capture.session-replay") }, - private val replayCapture: ReplayCapture = ReplayModule.replayDependencies.replayCapture, - private val logger: ReplayLogger, + ) { fun captureScreen(skipReplayComposeViews: Boolean) { mainThreadHandler.run { diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt index 6c068c2f..928b4c84 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt @@ -10,21 +10,20 @@ package io.bitdrift.capture.replay.internal import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.replay.L -import io.bitdrift.capture.replay.ReplayModule +//import io.bitdrift.capture.replay.L import io.bitdrift.capture.replay.ReplayType // Add the screen and keyboard layouts to the replay capture internal class ReplayDecorations( - errorHandler: ErrorHandler = ReplayModule.replayDependencies.errorHandler, - private val displayManager: DisplayManagers = ReplayModule.replayDependencies.displayManager, + errorHandler: ErrorHandler, + private val displayManager: DisplayManagers, ) { private val windowManager = WindowManager(errorHandler) fun addDecorations(filteredCapture: FilteredCapture): FilteredCapture { // Add screen size as the first element val bounds = displayManager.refreshDisplay() - L.d("Display Screen size $bounds") +// L.d("Display Screen size $bounds") val screen: MutableList = mutableListOf(bounds) screen.addAll(filteredCapture) @@ -43,7 +42,7 @@ internal class ReplayDecorations( width = rootView.width, height = insets.bottom, ) - L.d("Keyboard IME size $imeBounds") +// L.d("Keyboard IME size $imeBounds") screen.add(imeBounds) } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDependencies.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDependencies.kt index 9bbc20d6..478543d8 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDependencies.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDependencies.kt @@ -11,14 +11,14 @@ import io.bitdrift.capture.common.ErrorHandler import io.bitdrift.capture.replay.ReplayLogger import io.bitdrift.capture.replay.SessionReplayConfiguration -internal class ReplayDependencies( - val errorHandler: ErrorHandler, - val logger: ReplayLogger, - val sessionReplayConfiguration: SessionReplayConfiguration, -) { - val displayManager: DisplayManagers = DisplayManagers() - - val replayCapture: ReplayCapture by lazy { - ReplayCapture() - } -} +//internal class ReplayDependencies( +// val errorHandler: ErrorHandler, +// val logger: ReplayLogger, +// val sessionReplayConfiguration: SessionReplayConfiguration, +//) { +// val displayManager: DisplayManagers = DisplayManagers() +// +// val replayCapture: ReplayCapture by lazy { +// ReplayCapture() +// } +//} diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt index ba0a55a5..924106a9 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt @@ -8,15 +8,16 @@ package io.bitdrift.capture.replay.internal import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.replay.L -import io.bitdrift.capture.replay.ReplayModule +import io.bitdrift.capture.replay.SessionReplayConfiguration +//import io.bitdrift.capture.replay.L import io.bitdrift.capture.replay.internal.mappers.ViewMapper internal typealias Capture = List> internal class ReplayParser( - private val errorHandler: ErrorHandler = ReplayModule.replayDependencies.errorHandler, - private val viewMapper: ViewMapper = ViewMapper(), + sessionReplayConfiguration: SessionReplayConfiguration, + private val errorHandler: ErrorHandler, + private val viewMapper: ViewMapper = ViewMapper(sessionReplayConfiguration), ) { private val windowManager = WindowManager(errorHandler) @@ -30,7 +31,7 @@ internal class ReplayParser( // Use a stack to perform a DFS traversal of the tree and avoid recursion val stack: ArrayDeque = ArrayDeque( windowManager.findRootViews().map { - L.v("Root view found and added to list: ${it.javaClass.simpleName}") +// L.v("Root view found and added to list: ${it.javaClass.simpleName}") ScannableView.AndroidView(it, skipReplayComposeViews) }, ) @@ -39,13 +40,13 @@ internal class ReplayParser( try { viewMapper.updateMetrics(currentNode, encodedScreenMetrics) if (!viewMapper.viewIsVisible(currentNode)) { - L.v("Ignoring not visible view: ${currentNode.displayName}") +// L.v("Ignoring not visible view: ${currentNode.displayName}") continue } result.add(viewMapper.mapView(currentNode)) } catch (e: Throwable) { val errorMsg = "Error parsing view, Skipping $currentNode and children" - L.e(e, errorMsg) +// L.e(e, errorMsg) encodedScreenMetrics.exceptionCausingViewCount += 1 errorHandler.handleError(errorMsg, e) } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ViewMapperConfiguration.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ViewMapperConfiguration.kt index 959727db..bb817342 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ViewMapperConfiguration.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ViewMapperConfiguration.kt @@ -7,12 +7,13 @@ package io.bitdrift.capture.replay.internal -import io.bitdrift.capture.replay.ReplayModule import io.bitdrift.capture.replay.ReplayType +import io.bitdrift.capture.replay.SessionReplayConfiguration internal class ViewMapperConfiguration( + sessionReplayConfiguration: SessionReplayConfiguration, private val externalMapper: Map>? = - ReplayModule.replayDependencies.sessionReplayConfiguration.replayMapperConfiguration?.customMapper, + sessionReplayConfiguration.replayMapperConfiguration?.customMapper, ) { /** diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt index 5a91f58d..7785b033 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt @@ -11,7 +11,7 @@ import android.os.Build import android.view.View import android.view.inspector.WindowInspector import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.replay.L +//import io.bitdrift.capture.replay.L // Used for retrieving the view hierarchies internal class WindowManager(private val errorHandler: ErrorHandler) { @@ -49,7 +49,7 @@ internal class WindowManager(private val errorHandler: ErrorHandler) { @Suppress("UNCHECKED_CAST") return getWindowViews.get(windowManagerGlobal) as List } catch (e: Throwable) { - L.e(e, "Failed to retrieve windows") +// L.e(e, "Failed to retrieve windows") errorHandler.handleError("Failed to retrieve windows", e) return emptyList() } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt index 365eb842..adffd22b 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt @@ -21,7 +21,7 @@ import androidx.compose.ui.semantics.SemanticsProperties import androidx.compose.ui.semantics.getOrNull import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.unit.toSize -import io.bitdrift.capture.replay.L +//import io.bitdrift.capture.replay.L import io.bitdrift.capture.replay.ReplayType import io.bitdrift.capture.replay.internal.ReplayRect import io.bitdrift.capture.replay.internal.ScannableView @@ -34,11 +34,11 @@ internal object ComposeTreeParser { val semanticsOwner = if (androidComposeView is AndroidComposeView) { androidComposeView.semanticsOwner } else { - L.e(null, "View passed to ComposeTreeParser.parse() is not an AndroidComposeView. view=${androidComposeView.javaClass.name}") +// L.e(null, "View passed to ComposeTreeParser.parse() is not an AndroidComposeView. view=${androidComposeView.javaClass.name}") return ScannableView.IgnoredComposeView } val rootNode = semanticsOwner.unmergedRootSemanticsNode - L.d("Found Compose SemanticsNode root. Parsing Compose tree.") +// L.d("Found Compose SemanticsNode root. Parsing Compose tree.") return rootNode.toScannableView() } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt index bc1cd76d..4dda6bbf 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt @@ -9,14 +9,16 @@ package io.bitdrift.capture.replay.internal.mappers import android.content.res.Resources import android.view.View -import io.bitdrift.capture.replay.L +import io.bitdrift.capture.replay.SessionReplayConfiguration +//import io.bitdrift.capture.replay.L import io.bitdrift.capture.replay.internal.EncodedScreenMetrics import io.bitdrift.capture.replay.internal.ReplayRect import io.bitdrift.capture.replay.internal.ScannableView import io.bitdrift.capture.replay.internal.ViewMapperConfiguration internal class ViewMapper( - private val viewMapperConfiguration: ViewMapperConfiguration = ViewMapperConfiguration(), + sessionReplayConfiguration: SessionReplayConfiguration, + private val viewMapperConfiguration: ViewMapperConfiguration = ViewMapperConfiguration(sessionReplayConfiguration), private val buttonMapper: ButtonMapper = ButtonMapper(), private val textMapper: TextMapper = TextMapper(), private val backgroundMapper: BackgroundMapper = BackgroundMapper(), @@ -62,17 +64,18 @@ internal class ViewMapper( private fun View.viewToReplayRect(): List { val list = mutableListOf() - val resourceName = if (id != -1) { - try { - resources.getResourceEntryName(this.id) - } catch (ignore: Resources.NotFoundException) { +// val resourceName = if (id != -1) { + try { + resources.getResourceEntryName(this.id) + } catch (ignore: Resources.NotFoundException) { // Do nothing. - L.e(ignore, "Ignoring view due to:${ignore.message} for ${this.id}") - "Failed to retrieve ID" - } - } else { - "no_resource_id" +// L.e(ignore, "Ignoring view due to:${ignore.message} for ${this.id}") } +// "Failed to retrieve ID" +// } +// } else { +// "no_resource_id" +// } val type = viewMapperConfiguration.mapper[this.javaClass.simpleName] if (type == null) { @@ -80,21 +83,21 @@ internal class ViewMapper( list.addAll(buttonMapper.map(this)) list.addAll(textMapper.map(this)) list.addAll(backgroundMapper.map(this)) - if (list.isEmpty()) { - L.v( - "Ignoring Unknown view: $resourceName ${this.javaClass.simpleName}:" + - " w=${this.width}, h=${this.height}", - ) - } else { - L.v("Matched ${list.size} views with ButtonMapper and TextMapper and BackgroundMapper") - } +// if (list.isEmpty()) { +//// L.v( +//// "Ignoring Unknown view: $resourceName ${this.javaClass.simpleName}:" + +//// " w=${this.width}, h=${this.height}", +//// ) +// } else { +//// L.v("Matched ${list.size} views with ButtonMapper and TextMapper and BackgroundMapper") +// } } else { val out = IntArray(2) this.getLocationOnScreen(out) - L.v( - "Successfully mapped Android view=${this.javaClass.simpleName} to=$type:" + - " ${out[0]}, ${out[1]}, ${this.width}, ${this.height}", - ) +// L.v( +// "Successfully mapped Android view=${this.javaClass.simpleName} to=$type:" + +// " ${out[0]}, ${out[1]}, ${this.width}, ${this.height}", +// ) list.add(ReplayRect(type, out[0], out[1], this.width, this.height)) } return list From d0e440d4f77e6acac240d1e240656bb404381980 Mon Sep 17 00:00:00 2001 From: Miguel Juarez Lopez Date: Thu, 7 Nov 2024 15:13:41 -0500 Subject: [PATCH 02/12] Actually get rid of ReplayDependencies file and references --- .../bitdrift/capture/replay/ReplayModule.kt | 21 ++++------------ .../replay/internal/ReplayDependencies.kt | 24 ------------------- 2 files changed, 5 insertions(+), 40 deletions(-) delete mode 100644 platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDependencies.kt diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt index c2f8a775..05bc1417 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt @@ -48,21 +48,15 @@ class ReplayModule( context: Context, mainThreadHandler: MainThreadHandler = MainThreadHandler(), ) { - private var replayCaptureController: ReplayCaptureController - internal val displayManager: DisplayManagers = DisplayManagers() - - private val replayCapture: ReplayCapture by lazy { - ReplayCapture(sessionReplayConfiguration, errorHandler, displayManager) - } + internal val displayManager: DisplayManagers + private val replayCapture: ReplayCapture + private val replayCaptureController: ReplayCaptureController init { -// replayDependencies = ReplayDependencies( -// errorHandler = errorHandler, -// logger = logger, -// sessionReplayConfiguration = sessionReplayConfiguration, -// ) + displayManager = DisplayManagers() displayManager.init(context) + replayCapture = ReplayCapture(sessionReplayConfiguration, errorHandler, displayManager) replayCaptureController = ReplayCaptureController(replayCapture, logger, mainThreadHandler) } @@ -73,9 +67,4 @@ class ReplayModule( fun captureScreen(skipReplayComposeViews: Boolean) { replayCaptureController.captureScreen(skipReplayComposeViews) } - -// companion object { -// // TODO(murki): Refactor dependencies to not rely on singleton god state -// internal lateinit var replayDependencies: ReplayDependencies -// } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDependencies.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDependencies.kt deleted file mode 100644 index 478543d8..00000000 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDependencies.kt +++ /dev/null @@ -1,24 +0,0 @@ -// capture-sdk - bitdrift's client SDK -// Copyright Bitdrift, Inc. All rights reserved. -// -// Use of this source code is governed by a source available license that can be found in the -// LICENSE file or at: -// https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt - -package io.bitdrift.capture.replay.internal - -import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.replay.ReplayLogger -import io.bitdrift.capture.replay.SessionReplayConfiguration - -//internal class ReplayDependencies( -// val errorHandler: ErrorHandler, -// val logger: ReplayLogger, -// val sessionReplayConfiguration: SessionReplayConfiguration, -//) { -// val displayManager: DisplayManagers = DisplayManagers() -// -// val replayCapture: ReplayCapture by lazy { -// ReplayCapture() -// } -//} From a3aa74085a0f34026d26a63e6cfd7a52d54c37cd Mon Sep 17 00:00:00 2001 From: Miguel Juarez Lopez Date: Thu, 7 Nov 2024 15:24:56 -0500 Subject: [PATCH 03/12] Pass 2: Restore internal replay diagnostic logging --- .../capture/events/SessionReplayTarget.kt | 1 - .../bitdrift/capture/replay/ReplayModule.kt | 40 ++++++++--------- .../capture/replay/internal/ReplayCapture.kt | 4 +- .../internal/ReplayCaptureController.kt | 2 - .../replay/internal/ReplayDecorations.kt | 6 +-- .../capture/replay/internal/ReplayParser.kt | 8 ++-- .../capture/replay/internal/WindowManager.kt | 4 +- .../internal/compose/ComposeTreeParser.kt | 6 +-- .../replay/internal/mappers/ViewMapper.kt | 45 +++++++++---------- 9 files changed, 54 insertions(+), 62 deletions(-) diff --git a/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt b/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt index 1c0c2cc3..c5609bfb 100644 --- a/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt +++ b/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt @@ -16,7 +16,6 @@ import io.bitdrift.capture.common.ErrorHandler import io.bitdrift.capture.common.MainThreadHandler import io.bitdrift.capture.common.Runtime import io.bitdrift.capture.common.RuntimeFeature -import io.bitdrift.capture.providers.FieldValue import io.bitdrift.capture.providers.toFieldValue import io.bitdrift.capture.providers.toFields import io.bitdrift.capture.replay.ReplayLogger diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt index 05bc1417..edf3af69 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt @@ -13,26 +13,6 @@ import io.bitdrift.capture.common.MainThreadHandler import io.bitdrift.capture.replay.internal.DisplayManagers import io.bitdrift.capture.replay.internal.ReplayCapture import io.bitdrift.capture.replay.internal.ReplayCaptureController -//import io.bitdrift.capture.replay.internal.ReplayDependencies - -// TODO(murki): [Replay] Re-enable internal diagnostic logging -// This is the logger called from the replay module source code. -//internal typealias L = ReplayModuleInternalLogs - -//internal object ReplayModuleInternalLogs { -// -// fun v(message: String) { -// ReplayModule.replayDependencies.logger.logVerboseInternal(message) -// } -// -// fun d(message: String) { -// ReplayModule.replayDependencies.logger.logDebugInternal(message) -// } -// -// fun e(e: Throwable?, message: String) { -// ReplayModule.replayDependencies.logger.logErrorInternal(message, e) -// } -//} /** * Sets up and controls the replay feature @@ -48,12 +28,12 @@ class ReplayModule( context: Context, mainThreadHandler: MainThreadHandler = MainThreadHandler(), ) { - internal val displayManager: DisplayManagers private val replayCapture: ReplayCapture private val replayCaptureController: ReplayCaptureController init { + L.logger = logger displayManager = DisplayManagers() displayManager.init(context) replayCapture = ReplayCapture(sessionReplayConfiguration, errorHandler, displayManager) @@ -62,9 +42,25 @@ class ReplayModule( /** * Prepares and emits a session replay screen log using a logger instance passed - * at initialiation time. + * at initialization time. */ fun captureScreen(skipReplayComposeViews: Boolean) { replayCaptureController.captureScreen(skipReplayComposeViews) } + + internal object L { + internal var logger: ReplayLogger? = null + + fun v(message: String) { + logger?.logVerboseInternal(message) + } + + fun d(message: String) { + logger?.logDebugInternal(message) + } + + fun e(e: Throwable?, message: String) { + logger?.logErrorInternal(message, e) + } + } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt index 56eb0e63..6ee13431 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt @@ -10,8 +10,8 @@ package io.bitdrift.capture.replay.internal import io.bitdrift.capture.common.DefaultClock import io.bitdrift.capture.common.ErrorHandler import io.bitdrift.capture.common.IClock +import io.bitdrift.capture.replay.ReplayModule import io.bitdrift.capture.replay.SessionReplayConfiguration -//import io.bitdrift.capture.replay.L import java.util.concurrent.ExecutorService import kotlin.time.measureTimedValue @@ -46,7 +46,7 @@ internal class ReplayCapture( val screen = captureDecorations.addDecorations(filteredCapture) val encodedScreen = replayEncoder.encode(screen) encodedScreenMetrics.captureTimeMs = clock.elapsedRealtime() - startTime -// L.d("Screen Captured: $encodedScreenMetrics") + ReplayModule.L.d("Screen Captured: $encodedScreenMetrics") completion(encodedScreen, screen, encodedScreenMetrics) } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureController.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureController.kt index a0822aee..e940a93d 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureController.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureController.kt @@ -9,8 +9,6 @@ package io.bitdrift.capture.replay.internal import io.bitdrift.capture.common.MainThreadHandler import io.bitdrift.capture.replay.ReplayLogger -import io.bitdrift.capture.replay.ReplayModule -import io.bitdrift.capture.replay.SessionReplayConfiguration import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt index 928b4c84..6b6d780d 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt @@ -10,7 +10,7 @@ package io.bitdrift.capture.replay.internal import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import io.bitdrift.capture.common.ErrorHandler -//import io.bitdrift.capture.replay.L +import io.bitdrift.capture.replay.ReplayModule import io.bitdrift.capture.replay.ReplayType // Add the screen and keyboard layouts to the replay capture @@ -23,7 +23,7 @@ internal class ReplayDecorations( fun addDecorations(filteredCapture: FilteredCapture): FilteredCapture { // Add screen size as the first element val bounds = displayManager.refreshDisplay() -// L.d("Display Screen size $bounds") + ReplayModule.L.d("Display Screen size $bounds") val screen: MutableList = mutableListOf(bounds) screen.addAll(filteredCapture) @@ -42,7 +42,7 @@ internal class ReplayDecorations( width = rootView.width, height = insets.bottom, ) -// L.d("Keyboard IME size $imeBounds") + ReplayModule.L.d("Keyboard IME size $imeBounds") screen.add(imeBounds) } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt index 924106a9..011b1d18 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt @@ -8,8 +8,8 @@ package io.bitdrift.capture.replay.internal import io.bitdrift.capture.common.ErrorHandler +import io.bitdrift.capture.replay.ReplayModule import io.bitdrift.capture.replay.SessionReplayConfiguration -//import io.bitdrift.capture.replay.L import io.bitdrift.capture.replay.internal.mappers.ViewMapper internal typealias Capture = List> @@ -31,7 +31,7 @@ internal class ReplayParser( // Use a stack to perform a DFS traversal of the tree and avoid recursion val stack: ArrayDeque = ArrayDeque( windowManager.findRootViews().map { -// L.v("Root view found and added to list: ${it.javaClass.simpleName}") + ReplayModule.L.v("Root view found and added to list: ${it.javaClass.simpleName}") ScannableView.AndroidView(it, skipReplayComposeViews) }, ) @@ -40,13 +40,13 @@ internal class ReplayParser( try { viewMapper.updateMetrics(currentNode, encodedScreenMetrics) if (!viewMapper.viewIsVisible(currentNode)) { -// L.v("Ignoring not visible view: ${currentNode.displayName}") + ReplayModule.L.v("Ignoring not visible view: ${currentNode.displayName}") continue } result.add(viewMapper.mapView(currentNode)) } catch (e: Throwable) { val errorMsg = "Error parsing view, Skipping $currentNode and children" -// L.e(e, errorMsg) + ReplayModule.L.e(e, errorMsg) encodedScreenMetrics.exceptionCausingViewCount += 1 errorHandler.handleError(errorMsg, e) } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt index 7785b033..cfa9541a 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt @@ -11,7 +11,7 @@ import android.os.Build import android.view.View import android.view.inspector.WindowInspector import io.bitdrift.capture.common.ErrorHandler -//import io.bitdrift.capture.replay.L +import io.bitdrift.capture.replay.ReplayModule // Used for retrieving the view hierarchies internal class WindowManager(private val errorHandler: ErrorHandler) { @@ -49,7 +49,7 @@ internal class WindowManager(private val errorHandler: ErrorHandler) { @Suppress("UNCHECKED_CAST") return getWindowViews.get(windowManagerGlobal) as List } catch (e: Throwable) { -// L.e(e, "Failed to retrieve windows") + ReplayModule.L.e(e, "Failed to retrieve windows") errorHandler.handleError("Failed to retrieve windows", e) return emptyList() } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt index adffd22b..c5992a94 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt @@ -21,7 +21,7 @@ import androidx.compose.ui.semantics.SemanticsProperties import androidx.compose.ui.semantics.getOrNull import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.unit.toSize -//import io.bitdrift.capture.replay.L +import io.bitdrift.capture.replay.ReplayModule import io.bitdrift.capture.replay.ReplayType import io.bitdrift.capture.replay.internal.ReplayRect import io.bitdrift.capture.replay.internal.ScannableView @@ -34,11 +34,11 @@ internal object ComposeTreeParser { val semanticsOwner = if (androidComposeView is AndroidComposeView) { androidComposeView.semanticsOwner } else { -// L.e(null, "View passed to ComposeTreeParser.parse() is not an AndroidComposeView. view=${androidComposeView.javaClass.name}") + ReplayModule.L.e(null, "View passed to ComposeTreeParser.parse() is not an AndroidComposeView. view=${androidComposeView.javaClass.name}") return ScannableView.IgnoredComposeView } val rootNode = semanticsOwner.unmergedRootSemanticsNode -// L.d("Found Compose SemanticsNode root. Parsing Compose tree.") + ReplayModule.L.d("Found Compose SemanticsNode root. Parsing Compose tree.") return rootNode.toScannableView() } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt index 4dda6bbf..c99a5177 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt @@ -9,8 +9,8 @@ package io.bitdrift.capture.replay.internal.mappers import android.content.res.Resources import android.view.View +import io.bitdrift.capture.replay.ReplayModule import io.bitdrift.capture.replay.SessionReplayConfiguration -//import io.bitdrift.capture.replay.L import io.bitdrift.capture.replay.internal.EncodedScreenMetrics import io.bitdrift.capture.replay.internal.ReplayRect import io.bitdrift.capture.replay.internal.ScannableView @@ -64,18 +64,17 @@ internal class ViewMapper( private fun View.viewToReplayRect(): List { val list = mutableListOf() -// val resourceName = if (id != -1) { - try { - resources.getResourceEntryName(this.id) - } catch (ignore: Resources.NotFoundException) { + val resourceName = if (id != -1) { + try { + resources.getResourceEntryName(this.id) + } catch (ignore: Resources.NotFoundException) { // Do nothing. -// L.e(ignore, "Ignoring view due to:${ignore.message} for ${this.id}") + ReplayModule.L.e(ignore, "Ignoring view due to:${ignore.message} for ${this.id}") + "Failed to retrieve ID" + } + } else { + "no_resource_id" } -// "Failed to retrieve ID" -// } -// } else { -// "no_resource_id" -// } val type = viewMapperConfiguration.mapper[this.javaClass.simpleName] if (type == null) { @@ -83,21 +82,21 @@ internal class ViewMapper( list.addAll(buttonMapper.map(this)) list.addAll(textMapper.map(this)) list.addAll(backgroundMapper.map(this)) -// if (list.isEmpty()) { -//// L.v( -//// "Ignoring Unknown view: $resourceName ${this.javaClass.simpleName}:" + -//// " w=${this.width}, h=${this.height}", -//// ) -// } else { -//// L.v("Matched ${list.size} views with ButtonMapper and TextMapper and BackgroundMapper") -// } + if (list.isEmpty()) { + ReplayModule.L.v( + "Ignoring Unknown view: $resourceName ${this.javaClass.simpleName}:" + + " w=${this.width}, h=${this.height}", + ) + } else { + ReplayModule.L.v("Matched ${list.size} views with ButtonMapper and TextMapper and BackgroundMapper") + } } else { val out = IntArray(2) this.getLocationOnScreen(out) -// L.v( -// "Successfully mapped Android view=${this.javaClass.simpleName} to=$type:" + -// " ${out[0]}, ${out[1]}, ${this.width}, ${this.height}", -// ) + ReplayModule.L.v( + "Successfully mapped Android view=${this.javaClass.simpleName} to=$type:" + + " ${out[0]}, ${out[1]}, ${this.width}, ${this.height}", + ) list.add(ReplayRect(type, out[0], out[1], this.width, this.height)) } return list From c30ab9044c29fe561ec996e89d9587dbfc1a05e7 Mon Sep 17 00:00:00 2001 From: Miguel Juarez Lopez Date: Thu, 7 Nov 2024 15:48:50 -0500 Subject: [PATCH 04/12] Pass 3: Refactor ReplayPreviewClient --- examples/android/MainActivity.kt | 4 +- .../capture/events/SessionReplayTarget.kt | 14 ++-- .../io/bitdrift/gradletestapp/TestUtils.kt | 83 +++++++++---------- .../{ReplayModule.kt => ReplayManager.kt} | 14 ++-- .../capture/replay/ReplayPreviewClient.kt | 29 +++++-- .../capture/replay/internal/ReplayCapture.kt | 4 +- .../replay/internal/ReplayDecorations.kt | 6 +- .../capture/replay/internal/ReplayParser.kt | 8 +- .../capture/replay/internal/WindowManager.kt | 4 +- .../internal/compose/ComposeTreeParser.kt | 6 +- .../replay/internal/mappers/ViewMapper.kt | 10 +-- 11 files changed, 93 insertions(+), 89 deletions(-) rename platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/{ReplayModule.kt => ReplayManager.kt} (82%) diff --git a/examples/android/MainActivity.kt b/examples/android/MainActivity.kt index 6d48e6e3..df0d0cab 100644 --- a/examples/android/MainActivity.kt +++ b/examples/android/MainActivity.kt @@ -52,7 +52,7 @@ import kotlin.time.toDuration class MainActivity : ComponentActivity() { private val replayPreviewClient: ReplayPreviewClient by lazy { - ReplayPreviewClient(ReplayModule( + ReplayPreviewClient( object: ErrorHandler { override fun handleError(detail: String, e: Throwable?) { Log.e("HelloWorldApp", "Replay handleError: $detail $e") @@ -83,7 +83,7 @@ class MainActivity : ComponentActivity() { }, SessionReplayConfiguration(), this.applicationContext - )) + ) } private lateinit var clipboardManager: ClipboardManager private lateinit var client: OkHttpClient diff --git a/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt b/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt index c5609bfb..2799516d 100644 --- a/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt +++ b/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt @@ -19,7 +19,7 @@ import io.bitdrift.capture.common.RuntimeFeature import io.bitdrift.capture.providers.toFieldValue import io.bitdrift.capture.providers.toFields import io.bitdrift.capture.replay.ReplayLogger -import io.bitdrift.capture.replay.ReplayModule +import io.bitdrift.capture.replay.ReplayManager import io.bitdrift.capture.replay.SessionReplayConfiguration import io.bitdrift.capture.replay.internal.EncodedScreenMetrics import io.bitdrift.capture.replay.internal.FilteredCapture @@ -33,10 +33,10 @@ internal class SessionReplayTarget( mainThreadHandler: MainThreadHandler = MainThreadHandler(), ) : ISessionReplayTarget, ReplayLogger { // TODO(Augustyniak): Make non nullable and pass at initialization time after - // `sessionReplayTarget` argument is moved from logger creation time to logger start time. - // Refer to TODO in `LoggerImpl` for more details. + // `sessionReplayTarget` argument is moved from logger creation time to logger start time. + // Refer to TODO in `LoggerImpl` for more details. internal var runtime: Runtime? = null - private val replayModule: ReplayModule = ReplayModule( + private val replayManager: ReplayManager = ReplayManager( errorHandler, this, configuration, @@ -49,13 +49,13 @@ internal class SessionReplayTarget( runtime?.isEnabled(RuntimeFeature.SESSION_REPLAY_COMPOSE) ?: RuntimeFeature.SESSION_REPLAY_COMPOSE.defaultValue ) - replayModule.captureScreen(skipReplayComposeViews) + replayManager.captureScreen(skipReplayComposeViews) } override fun captureScreenshot() { // TODO(Augustyniak): Implement this method to add support for screenshot capture on Android. - // As currently implemented, the function must emit a session replay screenshot log. - // Without this emission, the SDK is blocked from requesting additional screenshots. + // As currently implemented, the function must emit a session replay screenshot log. + // Without this emission, the SDK is blocked from requesting additional screenshots. } override fun onScreenCaptured(encodedScreen: ByteArray, screen: FilteredCapture, metrics: EncodedScreenMetrics) { diff --git a/platform/jvm/gradle-test-app/src/androidTest/java/io/bitdrift/gradletestapp/TestUtils.kt b/platform/jvm/gradle-test-app/src/androidTest/java/io/bitdrift/gradletestapp/TestUtils.kt index a86d54c5..718d5e75 100644 --- a/platform/jvm/gradle-test-app/src/androidTest/java/io/bitdrift/gradletestapp/TestUtils.kt +++ b/platform/jvm/gradle-test-app/src/androidTest/java/io/bitdrift/gradletestapp/TestUtils.kt @@ -11,11 +11,7 @@ import android.content.Context import android.util.Base64 import android.util.Log import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.common.MainThreadHandler -import io.bitdrift.capture.common.Runtime -import io.bitdrift.capture.common.RuntimeFeature import io.bitdrift.capture.replay.ReplayLogger -import io.bitdrift.capture.replay.ReplayModule import io.bitdrift.capture.replay.ReplayPreviewClient import io.bitdrift.capture.replay.SessionReplayConfiguration import io.bitdrift.capture.replay.internal.EncodedScreenMetrics @@ -31,51 +27,48 @@ object TestUtils { context: Context ): ReplayPreviewClient { return ReplayPreviewClient( - ReplayModule( - object : ErrorHandler { - override fun handleError(detail: String, e: Throwable?) { - Log.e("Replay Tests", "error: $detail $e") - } - }, - object : ReplayLogger { - override fun onScreenCaptured( - encodedScreen: ByteArray, - screen: FilteredCapture, - metrics: EncodedScreenMetrics - ) { - Log.d("Replay Tests", "took ${metrics.captureTimeMs}ms") - Log.d("Replay Tests", "Captured a total of ${screen.size} ReplayRect views.") - Log.d("Replay Tests", screen.toString()) - Log.d( - "Replay Tests", - "echo \"${ - Base64.encodeToString( - encodedScreen, - 0 - ) - }\" | websocat ws://localhost:3001 --base64 -bv -1" - ) + object : ErrorHandler { + override fun handleError(detail: String, e: Throwable?) { + Log.e("Replay Tests", "error: $detail $e") + } + }, + object : ReplayLogger { + override fun onScreenCaptured( + encodedScreen: ByteArray, + screen: FilteredCapture, + metrics: EncodedScreenMetrics + ) { + Log.d("Replay Tests", "took ${metrics.captureTimeMs}ms") + Log.d("Replay Tests", "Captured a total of ${screen.size} ReplayRect views.") + Log.d("Replay Tests", screen.toString()) + Log.d( + "Replay Tests", + "echo \"${ + Base64.encodeToString( + encodedScreen, + 0 + ) + }\" | websocat ws://localhost:3001 --base64 -bv -1" + ) - replay.set(Pair(screen, metrics)) - latch.countDown() - } + replay.set(Pair(screen, metrics)) + latch.countDown() + } - override fun logVerboseInternal(message: String, fields: Map?) { - Log.v("Replay Tests", message) - } + override fun logVerboseInternal(message: String, fields: Map?) { + Log.v("Replay Tests", message) + } - override fun logDebugInternal(message: String, fields: Map?) { - Log.d("Replay Tests", message) - } + override fun logDebugInternal(message: String, fields: Map?) { + Log.d("Replay Tests", message) + } - override fun logErrorInternal(message: String, e: Throwable?, fields: Map?) { - Log.e("Replay Tests", message, e) - } - }, - SessionReplayConfiguration(), - context, - MainThreadHandler(), - ), + override fun logErrorInternal(message: String, e: Throwable?, fields: Map?) { + Log.e("Replay Tests", message, e) + } + }, + SessionReplayConfiguration(), + context, ) } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayManager.kt similarity index 82% rename from platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt rename to platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayManager.kt index edf3af69..7c2a1d48 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayModule.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayManager.kt @@ -21,22 +21,20 @@ import io.bitdrift.capture.replay.internal.ReplayCaptureController * @param sessionReplayConfiguration the configuration to use * @param runtime allows for the feature to be remotely disabled */ -class ReplayModule( - internal val errorHandler: ErrorHandler, - internal val logger: ReplayLogger, - internal val sessionReplayConfiguration: SessionReplayConfiguration, +class ReplayManager( + private val errorHandler: ErrorHandler, + logger: ReplayLogger, + private val sessionReplayConfiguration: SessionReplayConfiguration, context: Context, mainThreadHandler: MainThreadHandler = MainThreadHandler(), ) { - internal val displayManager: DisplayManagers - private val replayCapture: ReplayCapture private val replayCaptureController: ReplayCaptureController init { L.logger = logger - displayManager = DisplayManagers() + val displayManager = DisplayManagers() displayManager.init(context) - replayCapture = ReplayCapture(sessionReplayConfiguration, errorHandler, displayManager) + val replayCapture = ReplayCapture(sessionReplayConfiguration, errorHandler, displayManager) replayCaptureController = ReplayCaptureController(replayCapture, logger, mainThreadHandler) } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt index 5dbcc511..97639ca4 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt @@ -7,8 +7,11 @@ package io.bitdrift.capture.replay +import android.content.Context import android.util.Base64 import android.util.Log +import io.bitdrift.capture.common.ErrorHandler +import io.bitdrift.capture.replay.internal.DisplayManagers import io.bitdrift.capture.replay.internal.EncodedScreenMetrics import io.bitdrift.capture.replay.internal.FilteredCapture import io.bitdrift.capture.replay.internal.ReplayCapture @@ -25,20 +28,30 @@ import java.util.concurrent.TimeUnit /** * Allows to capture the screen and send the binary data over a persistent websocket connection - * @param replayModule The replay module to use for screen capture + * @param replayManager The replay module to use for screen capture * @param context The context of the app to capture * @param protocol The protocol to use for the websocket connection (default is ws) * @param host The host to connect to (default is Android's emulator loopback IP: 10.0.2.2) * @param port The port to connect to (default is 3001) */ class ReplayPreviewClient( - private val replayModule: ReplayModule, // TODO(murki): [Replay] Refactor to not expose this + errorHandler: ErrorHandler, + private val logger: ReplayLogger, + sessionReplayConfiguration: SessionReplayConfiguration, + context: Context, protocol: String = "ws", host: String = "10.0.2.2", port: Int = 3001, ) : ReplayLogger { - private val replayCapture: ReplayCapture = ReplayCapture(replayModule.sessionReplayConfiguration, replayModule.errorHandler, replayModule.displayManager) + private val replayCapture: ReplayCapture + + init { + val displayManager = DisplayManagers() + displayManager.init(context) + replayCapture = ReplayCapture(sessionReplayConfiguration, errorHandler, displayManager) + } + private val executor: ExecutorService = Executors.newSingleThreadExecutor { Thread(it, "io.bitdrift.capture.session-replay-client") } @@ -69,7 +82,7 @@ class ReplayPreviewClient( */ fun captureScreen() { replayCapture.captureScreen(executor, skipReplayComposeViews = false) { encodedScreen, screen, metrics -> - replayModule.logger.onScreenCaptured(encodedScreen, screen, metrics) + logger.onScreenCaptured(encodedScreen, screen, metrics) } } @@ -89,19 +102,19 @@ class ReplayPreviewClient( lastEncodedScreen = encodedScreen webSocket?.send(encodedScreen.toByteString(0, encodedScreen.size)) // forward the callback to the module's logger - replayModule.logger.onScreenCaptured(encodedScreen, screen, metrics) + logger.onScreenCaptured(encodedScreen, screen, metrics) } override fun logVerboseInternal(message: String, fields: Map?) { - replayModule.logger.logVerboseInternal(message, fields) + logger.logVerboseInternal(message, fields) } override fun logDebugInternal(message: String, fields: Map?) { - replayModule.logger.logDebugInternal(message, fields) + logger.logDebugInternal(message, fields) } override fun logErrorInternal(message: String, e: Throwable?, fields: Map?) { - replayModule.logger.logErrorInternal(message, e, fields) + logger.logErrorInternal(message, e, fields) } private object WebSocketLogger : WebSocketListener() { diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt index 6ee13431..773beda2 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt @@ -10,7 +10,7 @@ package io.bitdrift.capture.replay.internal import io.bitdrift.capture.common.DefaultClock import io.bitdrift.capture.common.ErrorHandler import io.bitdrift.capture.common.IClock -import io.bitdrift.capture.replay.ReplayModule +import io.bitdrift.capture.replay.ReplayManager import io.bitdrift.capture.replay.SessionReplayConfiguration import java.util.concurrent.ExecutorService import kotlin.time.measureTimedValue @@ -46,7 +46,7 @@ internal class ReplayCapture( val screen = captureDecorations.addDecorations(filteredCapture) val encodedScreen = replayEncoder.encode(screen) encodedScreenMetrics.captureTimeMs = clock.elapsedRealtime() - startTime - ReplayModule.L.d("Screen Captured: $encodedScreenMetrics") + ReplayManager.L.d("Screen Captured: $encodedScreenMetrics") completion(encodedScreen, screen, encodedScreenMetrics) } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt index 6b6d780d..91e13121 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt @@ -10,7 +10,7 @@ package io.bitdrift.capture.replay.internal import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.replay.ReplayModule +import io.bitdrift.capture.replay.ReplayManager import io.bitdrift.capture.replay.ReplayType // Add the screen and keyboard layouts to the replay capture @@ -23,7 +23,7 @@ internal class ReplayDecorations( fun addDecorations(filteredCapture: FilteredCapture): FilteredCapture { // Add screen size as the first element val bounds = displayManager.refreshDisplay() - ReplayModule.L.d("Display Screen size $bounds") + ReplayManager.L.d("Display Screen size $bounds") val screen: MutableList = mutableListOf(bounds) screen.addAll(filteredCapture) @@ -42,7 +42,7 @@ internal class ReplayDecorations( width = rootView.width, height = insets.bottom, ) - ReplayModule.L.d("Keyboard IME size $imeBounds") + ReplayManager.L.d("Keyboard IME size $imeBounds") screen.add(imeBounds) } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt index 011b1d18..ae627d9e 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt @@ -8,7 +8,7 @@ package io.bitdrift.capture.replay.internal import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.replay.ReplayModule +import io.bitdrift.capture.replay.ReplayManager import io.bitdrift.capture.replay.SessionReplayConfiguration import io.bitdrift.capture.replay.internal.mappers.ViewMapper @@ -31,7 +31,7 @@ internal class ReplayParser( // Use a stack to perform a DFS traversal of the tree and avoid recursion val stack: ArrayDeque = ArrayDeque( windowManager.findRootViews().map { - ReplayModule.L.v("Root view found and added to list: ${it.javaClass.simpleName}") + ReplayManager.L.v("Root view found and added to list: ${it.javaClass.simpleName}") ScannableView.AndroidView(it, skipReplayComposeViews) }, ) @@ -40,13 +40,13 @@ internal class ReplayParser( try { viewMapper.updateMetrics(currentNode, encodedScreenMetrics) if (!viewMapper.viewIsVisible(currentNode)) { - ReplayModule.L.v("Ignoring not visible view: ${currentNode.displayName}") + ReplayManager.L.v("Ignoring not visible view: ${currentNode.displayName}") continue } result.add(viewMapper.mapView(currentNode)) } catch (e: Throwable) { val errorMsg = "Error parsing view, Skipping $currentNode and children" - ReplayModule.L.e(e, errorMsg) + ReplayManager.L.e(e, errorMsg) encodedScreenMetrics.exceptionCausingViewCount += 1 errorHandler.handleError(errorMsg, e) } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt index cfa9541a..77693b58 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt @@ -11,7 +11,7 @@ import android.os.Build import android.view.View import android.view.inspector.WindowInspector import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.replay.ReplayModule +import io.bitdrift.capture.replay.ReplayManager // Used for retrieving the view hierarchies internal class WindowManager(private val errorHandler: ErrorHandler) { @@ -49,7 +49,7 @@ internal class WindowManager(private val errorHandler: ErrorHandler) { @Suppress("UNCHECKED_CAST") return getWindowViews.get(windowManagerGlobal) as List } catch (e: Throwable) { - ReplayModule.L.e(e, "Failed to retrieve windows") + ReplayManager.L.e(e, "Failed to retrieve windows") errorHandler.handleError("Failed to retrieve windows", e) return emptyList() } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt index c5992a94..2ee9dbdf 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt @@ -21,7 +21,7 @@ import androidx.compose.ui.semantics.SemanticsProperties import androidx.compose.ui.semantics.getOrNull import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.unit.toSize -import io.bitdrift.capture.replay.ReplayModule +import io.bitdrift.capture.replay.ReplayManager import io.bitdrift.capture.replay.ReplayType import io.bitdrift.capture.replay.internal.ReplayRect import io.bitdrift.capture.replay.internal.ScannableView @@ -34,11 +34,11 @@ internal object ComposeTreeParser { val semanticsOwner = if (androidComposeView is AndroidComposeView) { androidComposeView.semanticsOwner } else { - ReplayModule.L.e(null, "View passed to ComposeTreeParser.parse() is not an AndroidComposeView. view=${androidComposeView.javaClass.name}") + ReplayManager.L.e(null, "View passed to ComposeTreeParser.parse() is not an AndroidComposeView. view=${androidComposeView.javaClass.name}") return ScannableView.IgnoredComposeView } val rootNode = semanticsOwner.unmergedRootSemanticsNode - ReplayModule.L.d("Found Compose SemanticsNode root. Parsing Compose tree.") + ReplayManager.L.d("Found Compose SemanticsNode root. Parsing Compose tree.") return rootNode.toScannableView() } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt index c99a5177..22c099b2 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt @@ -9,7 +9,7 @@ package io.bitdrift.capture.replay.internal.mappers import android.content.res.Resources import android.view.View -import io.bitdrift.capture.replay.ReplayModule +import io.bitdrift.capture.replay.ReplayManager import io.bitdrift.capture.replay.SessionReplayConfiguration import io.bitdrift.capture.replay.internal.EncodedScreenMetrics import io.bitdrift.capture.replay.internal.ReplayRect @@ -69,7 +69,7 @@ internal class ViewMapper( resources.getResourceEntryName(this.id) } catch (ignore: Resources.NotFoundException) { // Do nothing. - ReplayModule.L.e(ignore, "Ignoring view due to:${ignore.message} for ${this.id}") + ReplayManager.L.e(ignore, "Ignoring view due to:${ignore.message} for ${this.id}") "Failed to retrieve ID" } } else { @@ -83,17 +83,17 @@ internal class ViewMapper( list.addAll(textMapper.map(this)) list.addAll(backgroundMapper.map(this)) if (list.isEmpty()) { - ReplayModule.L.v( + ReplayManager.L.v( "Ignoring Unknown view: $resourceName ${this.javaClass.simpleName}:" + " w=${this.width}, h=${this.height}", ) } else { - ReplayModule.L.v("Matched ${list.size} views with ButtonMapper and TextMapper and BackgroundMapper") + ReplayManager.L.v("Matched ${list.size} views with ButtonMapper and TextMapper and BackgroundMapper") } } else { val out = IntArray(2) this.getLocationOnScreen(out) - ReplayModule.L.v( + ReplayManager.L.v( "Successfully mapped Android view=${this.javaClass.simpleName} to=$type:" + " ${out[0]}, ${out[1]}, ${this.width}, ${this.height}", ) From a6be5ee74c2ca3393d3007850dac851090e8a7f5 Mon Sep 17 00:00:00 2001 From: Miguel Juarez Lopez Date: Thu, 7 Nov 2024 15:53:10 -0500 Subject: [PATCH 05/12] Pass 4: Flatten ReplayCaptureController --- .../capture/events/SessionReplayTarget.kt | 6 ++-- ...yManager.kt => ReplayCaptureController.kt} | 25 +++++++++------ .../capture/replay/ReplayPreviewClient.kt | 8 ++--- .../internal/ReplayCaptureController.kt | 32 ------------------- ...eplayCapture.kt => ReplayCaptureEngine.kt} | 6 ++-- .../replay/internal/ReplayDecorations.kt | 6 ++-- .../capture/replay/internal/ReplayParser.kt | 8 ++--- .../capture/replay/internal/WindowManager.kt | 4 +-- .../internal/compose/ComposeTreeParser.kt | 6 ++-- .../replay/internal/mappers/ViewMapper.kt | 10 +++--- 10 files changed, 43 insertions(+), 68 deletions(-) rename platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/{ReplayManager.kt => ReplayCaptureController.kt} (66%) delete mode 100644 platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureController.kt rename platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/{ReplayCapture.kt => ReplayCaptureEngine.kt} (92%) diff --git a/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt b/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt index 2799516d..38f525ab 100644 --- a/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt +++ b/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt @@ -19,7 +19,7 @@ import io.bitdrift.capture.common.RuntimeFeature import io.bitdrift.capture.providers.toFieldValue import io.bitdrift.capture.providers.toFields import io.bitdrift.capture.replay.ReplayLogger -import io.bitdrift.capture.replay.ReplayManager +import io.bitdrift.capture.replay.ReplayCaptureController import io.bitdrift.capture.replay.SessionReplayConfiguration import io.bitdrift.capture.replay.internal.EncodedScreenMetrics import io.bitdrift.capture.replay.internal.FilteredCapture @@ -36,7 +36,7 @@ internal class SessionReplayTarget( // `sessionReplayTarget` argument is moved from logger creation time to logger start time. // Refer to TODO in `LoggerImpl` for more details. internal var runtime: Runtime? = null - private val replayManager: ReplayManager = ReplayManager( + private val replayCaptureController: ReplayCaptureController = ReplayCaptureController( errorHandler, this, configuration, @@ -49,7 +49,7 @@ internal class SessionReplayTarget( runtime?.isEnabled(RuntimeFeature.SESSION_REPLAY_COMPOSE) ?: RuntimeFeature.SESSION_REPLAY_COMPOSE.defaultValue ) - replayManager.captureScreen(skipReplayComposeViews) + replayCaptureController.captureScreen(skipReplayComposeViews) } override fun captureScreenshot() { diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayManager.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt similarity index 66% rename from platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayManager.kt rename to platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt index 7c2a1d48..05a7c5e4 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayManager.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt @@ -11,8 +11,9 @@ import android.content.Context import io.bitdrift.capture.common.ErrorHandler import io.bitdrift.capture.common.MainThreadHandler import io.bitdrift.capture.replay.internal.DisplayManagers -import io.bitdrift.capture.replay.internal.ReplayCapture -import io.bitdrift.capture.replay.internal.ReplayCaptureController +import io.bitdrift.capture.replay.internal.ReplayCaptureEngine +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService /** * Sets up and controls the replay feature @@ -21,21 +22,23 @@ import io.bitdrift.capture.replay.internal.ReplayCaptureController * @param sessionReplayConfiguration the configuration to use * @param runtime allows for the feature to be remotely disabled */ -class ReplayManager( +class ReplayCaptureController( private val errorHandler: ErrorHandler, - logger: ReplayLogger, + private val logger: ReplayLogger, private val sessionReplayConfiguration: SessionReplayConfiguration, context: Context, - mainThreadHandler: MainThreadHandler = MainThreadHandler(), + private val mainThreadHandler: MainThreadHandler = MainThreadHandler(), + private val executor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor { + Thread(it, "io.bitdrift.capture.session-replay") + }, ) { - private val replayCaptureController: ReplayCaptureController + private val replayCaptureEngine: ReplayCaptureEngine init { L.logger = logger val displayManager = DisplayManagers() displayManager.init(context) - val replayCapture = ReplayCapture(sessionReplayConfiguration, errorHandler, displayManager) - replayCaptureController = ReplayCaptureController(replayCapture, logger, mainThreadHandler) + replayCaptureEngine = ReplayCaptureEngine(sessionReplayConfiguration, errorHandler, displayManager) } /** @@ -43,7 +46,11 @@ class ReplayManager( * at initialization time. */ fun captureScreen(skipReplayComposeViews: Boolean) { - replayCaptureController.captureScreen(skipReplayComposeViews) + mainThreadHandler.run { + replayCaptureEngine.captureScreen(executor, skipReplayComposeViews) { byteArray, screen, metrics -> + logger.onScreenCaptured(byteArray, screen, metrics) + } + } } internal object L { diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt index 97639ca4..80a737f1 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt @@ -14,7 +14,7 @@ import io.bitdrift.capture.common.ErrorHandler import io.bitdrift.capture.replay.internal.DisplayManagers import io.bitdrift.capture.replay.internal.EncodedScreenMetrics import io.bitdrift.capture.replay.internal.FilteredCapture -import io.bitdrift.capture.replay.internal.ReplayCapture +import io.bitdrift.capture.replay.internal.ReplayCaptureEngine import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response @@ -44,12 +44,12 @@ class ReplayPreviewClient( port: Int = 3001, ) : ReplayLogger { - private val replayCapture: ReplayCapture + private val replayCaptureEngine: ReplayCaptureEngine init { val displayManager = DisplayManagers() displayManager.init(context) - replayCapture = ReplayCapture(sessionReplayConfiguration, errorHandler, displayManager) + replayCaptureEngine = ReplayCaptureEngine(sessionReplayConfiguration, errorHandler, displayManager) } private val executor: ExecutorService = Executors.newSingleThreadExecutor { @@ -81,7 +81,7 @@ class ReplayPreviewClient( * Capture the screen and send it over the websocket connection after processing */ fun captureScreen() { - replayCapture.captureScreen(executor, skipReplayComposeViews = false) { encodedScreen, screen, metrics -> + replayCaptureEngine.captureScreen(executor, skipReplayComposeViews = false) { encodedScreen, screen, metrics -> logger.onScreenCaptured(encodedScreen, screen, metrics) } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureController.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureController.kt deleted file mode 100644 index e940a93d..00000000 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureController.kt +++ /dev/null @@ -1,32 +0,0 @@ -// capture-sdk - bitdrift's client SDK -// Copyright Bitdrift, Inc. All rights reserved. -// -// Use of this source code is governed by a source available license that can be found in the -// LICENSE file or at: -// https://polyformproject.org/wp-content/uploads/2020/06/PolyForm-Shield-1.0.0.txt - -package io.bitdrift.capture.replay.internal - -import io.bitdrift.capture.common.MainThreadHandler -import io.bitdrift.capture.replay.ReplayLogger -import java.util.concurrent.Executors -import java.util.concurrent.ScheduledExecutorService - -// Captures wireframe and pixel perfect representations of app's screen. -internal class ReplayCaptureController( - private val replayCapture: ReplayCapture, - private val logger: ReplayLogger, - private val mainThreadHandler: MainThreadHandler, - private val executor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor { - Thread(it, "io.bitdrift.capture.session-replay") - }, - -) { - fun captureScreen(skipReplayComposeViews: Boolean) { - mainThreadHandler.run { - replayCapture.captureScreen(executor, skipReplayComposeViews) { byteArray, screen, metrics -> - logger.onScreenCaptured(byteArray, screen, metrics) - } - } - } -} diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt similarity index 92% rename from platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt rename to platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt index 773beda2..6c1502ed 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCapture.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt @@ -10,13 +10,13 @@ package io.bitdrift.capture.replay.internal import io.bitdrift.capture.common.DefaultClock import io.bitdrift.capture.common.ErrorHandler import io.bitdrift.capture.common.IClock -import io.bitdrift.capture.replay.ReplayManager +import io.bitdrift.capture.replay.ReplayCaptureController import io.bitdrift.capture.replay.SessionReplayConfiguration import java.util.concurrent.ExecutorService import kotlin.time.measureTimedValue // This is the main logic for capturing a screen -internal class ReplayCapture( +internal class ReplayCaptureEngine( sessionReplayConfiguration: SessionReplayConfiguration, errorHandler: ErrorHandler, displayManager: DisplayManagers, @@ -46,7 +46,7 @@ internal class ReplayCapture( val screen = captureDecorations.addDecorations(filteredCapture) val encodedScreen = replayEncoder.encode(screen) encodedScreenMetrics.captureTimeMs = clock.elapsedRealtime() - startTime - ReplayManager.L.d("Screen Captured: $encodedScreenMetrics") + ReplayCaptureController.L.d("Screen Captured: $encodedScreenMetrics") completion(encodedScreen, screen, encodedScreenMetrics) } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt index 91e13121..678086cf 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayDecorations.kt @@ -10,7 +10,7 @@ package io.bitdrift.capture.replay.internal import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.replay.ReplayManager +import io.bitdrift.capture.replay.ReplayCaptureController import io.bitdrift.capture.replay.ReplayType // Add the screen and keyboard layouts to the replay capture @@ -23,7 +23,7 @@ internal class ReplayDecorations( fun addDecorations(filteredCapture: FilteredCapture): FilteredCapture { // Add screen size as the first element val bounds = displayManager.refreshDisplay() - ReplayManager.L.d("Display Screen size $bounds") + ReplayCaptureController.L.d("Display Screen size $bounds") val screen: MutableList = mutableListOf(bounds) screen.addAll(filteredCapture) @@ -42,7 +42,7 @@ internal class ReplayDecorations( width = rootView.width, height = insets.bottom, ) - ReplayManager.L.d("Keyboard IME size $imeBounds") + ReplayCaptureController.L.d("Keyboard IME size $imeBounds") screen.add(imeBounds) } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt index ae627d9e..1274698a 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayParser.kt @@ -8,7 +8,7 @@ package io.bitdrift.capture.replay.internal import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.replay.ReplayManager +import io.bitdrift.capture.replay.ReplayCaptureController import io.bitdrift.capture.replay.SessionReplayConfiguration import io.bitdrift.capture.replay.internal.mappers.ViewMapper @@ -31,7 +31,7 @@ internal class ReplayParser( // Use a stack to perform a DFS traversal of the tree and avoid recursion val stack: ArrayDeque = ArrayDeque( windowManager.findRootViews().map { - ReplayManager.L.v("Root view found and added to list: ${it.javaClass.simpleName}") + ReplayCaptureController.L.v("Root view found and added to list: ${it.javaClass.simpleName}") ScannableView.AndroidView(it, skipReplayComposeViews) }, ) @@ -40,13 +40,13 @@ internal class ReplayParser( try { viewMapper.updateMetrics(currentNode, encodedScreenMetrics) if (!viewMapper.viewIsVisible(currentNode)) { - ReplayManager.L.v("Ignoring not visible view: ${currentNode.displayName}") + ReplayCaptureController.L.v("Ignoring not visible view: ${currentNode.displayName}") continue } result.add(viewMapper.mapView(currentNode)) } catch (e: Throwable) { val errorMsg = "Error parsing view, Skipping $currentNode and children" - ReplayManager.L.e(e, errorMsg) + ReplayCaptureController.L.e(e, errorMsg) encodedScreenMetrics.exceptionCausingViewCount += 1 errorHandler.handleError(errorMsg, e) } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt index 77693b58..a7a652f4 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/WindowManager.kt @@ -11,7 +11,7 @@ import android.os.Build import android.view.View import android.view.inspector.WindowInspector import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.replay.ReplayManager +import io.bitdrift.capture.replay.ReplayCaptureController // Used for retrieving the view hierarchies internal class WindowManager(private val errorHandler: ErrorHandler) { @@ -49,7 +49,7 @@ internal class WindowManager(private val errorHandler: ErrorHandler) { @Suppress("UNCHECKED_CAST") return getWindowViews.get(windowManagerGlobal) as List } catch (e: Throwable) { - ReplayManager.L.e(e, "Failed to retrieve windows") + ReplayCaptureController.L.e(e, "Failed to retrieve windows") errorHandler.handleError("Failed to retrieve windows", e) return emptyList() } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt index 2ee9dbdf..2fe14526 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt @@ -21,7 +21,7 @@ import androidx.compose.ui.semantics.SemanticsProperties import androidx.compose.ui.semantics.getOrNull import androidx.compose.ui.state.ToggleableState import androidx.compose.ui.unit.toSize -import io.bitdrift.capture.replay.ReplayManager +import io.bitdrift.capture.replay.ReplayCaptureController import io.bitdrift.capture.replay.ReplayType import io.bitdrift.capture.replay.internal.ReplayRect import io.bitdrift.capture.replay.internal.ScannableView @@ -34,11 +34,11 @@ internal object ComposeTreeParser { val semanticsOwner = if (androidComposeView is AndroidComposeView) { androidComposeView.semanticsOwner } else { - ReplayManager.L.e(null, "View passed to ComposeTreeParser.parse() is not an AndroidComposeView. view=${androidComposeView.javaClass.name}") + ReplayCaptureController.L.e(null, "View passed to ComposeTreeParser.parse() is not an AndroidComposeView. view=${androidComposeView.javaClass.name}") return ScannableView.IgnoredComposeView } val rootNode = semanticsOwner.unmergedRootSemanticsNode - ReplayManager.L.d("Found Compose SemanticsNode root. Parsing Compose tree.") + ReplayCaptureController.L.d("Found Compose SemanticsNode root. Parsing Compose tree.") return rootNode.toScannableView() } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt index 22c099b2..0a3ace57 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/mappers/ViewMapper.kt @@ -9,7 +9,7 @@ package io.bitdrift.capture.replay.internal.mappers import android.content.res.Resources import android.view.View -import io.bitdrift.capture.replay.ReplayManager +import io.bitdrift.capture.replay.ReplayCaptureController import io.bitdrift.capture.replay.SessionReplayConfiguration import io.bitdrift.capture.replay.internal.EncodedScreenMetrics import io.bitdrift.capture.replay.internal.ReplayRect @@ -69,7 +69,7 @@ internal class ViewMapper( resources.getResourceEntryName(this.id) } catch (ignore: Resources.NotFoundException) { // Do nothing. - ReplayManager.L.e(ignore, "Ignoring view due to:${ignore.message} for ${this.id}") + ReplayCaptureController.L.e(ignore, "Ignoring view due to:${ignore.message} for ${this.id}") "Failed to retrieve ID" } } else { @@ -83,17 +83,17 @@ internal class ViewMapper( list.addAll(textMapper.map(this)) list.addAll(backgroundMapper.map(this)) if (list.isEmpty()) { - ReplayManager.L.v( + ReplayCaptureController.L.v( "Ignoring Unknown view: $resourceName ${this.javaClass.simpleName}:" + " w=${this.width}, h=${this.height}", ) } else { - ReplayManager.L.v("Matched ${list.size} views with ButtonMapper and TextMapper and BackgroundMapper") + ReplayCaptureController.L.v("Matched ${list.size} views with ButtonMapper and TextMapper and BackgroundMapper") } } else { val out = IntArray(2) this.getLocationOnScreen(out) - ReplayManager.L.v( + ReplayCaptureController.L.v( "Successfully mapped Android view=${this.javaClass.simpleName} to=$type:" + " ${out[0]}, ${out[1]}, ${this.width}, ${this.height}", ) From 50039823665b495cffa37664450c3629854f7f84 Mon Sep 17 00:00:00 2001 From: Miguel Juarez Lopez Date: Thu, 7 Nov 2024 15:59:29 -0500 Subject: [PATCH 06/12] Fix DisplayManagers deps --- .../io/bitdrift/capture/replay/ReplayCaptureController.kt | 4 +--- .../io/bitdrift/capture/replay/ReplayPreviewClient.kt | 8 +------- .../bitdrift/capture/replay/internal/DisplayManagers.kt | 8 ++------ .../capture/replay/internal/ReplayCaptureEngine.kt | 4 +++- 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt index 05a7c5e4..9112dcae 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt @@ -36,9 +36,7 @@ class ReplayCaptureController( init { L.logger = logger - val displayManager = DisplayManagers() - displayManager.init(context) - replayCaptureEngine = ReplayCaptureEngine(sessionReplayConfiguration, errorHandler, displayManager) + replayCaptureEngine = ReplayCaptureEngine(sessionReplayConfiguration, errorHandler, context) } /** diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt index 80a737f1..8ca5912d 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt @@ -44,13 +44,7 @@ class ReplayPreviewClient( port: Int = 3001, ) : ReplayLogger { - private val replayCaptureEngine: ReplayCaptureEngine - - init { - val displayManager = DisplayManagers() - displayManager.init(context) - replayCaptureEngine = ReplayCaptureEngine(sessionReplayConfiguration, errorHandler, displayManager) - } + private val replayCaptureEngine: ReplayCaptureEngine = ReplayCaptureEngine(sessionReplayConfiguration, errorHandler, context) private val executor: ExecutorService = Executors.newSingleThreadExecutor { Thread(it, "io.bitdrift.capture.session-replay-client") diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/DisplayManagers.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/DisplayManagers.kt index 3a8e9ed5..e81748f1 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/DisplayManagers.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/DisplayManagers.kt @@ -13,13 +13,9 @@ import android.util.DisplayMetrics import android.view.WindowManager import io.bitdrift.capture.replay.ReplayType -internal class DisplayManagers { +internal class DisplayManagers(context: Context) { - private lateinit var windowManager: WindowManager - - fun init(context: Context) { - windowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager - } + private var windowManager: WindowManager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager @Suppress("DEPRECATION") fun refreshDisplay(): ReplayRect { diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt index 6c1502ed..34a29fb2 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt @@ -7,6 +7,7 @@ package io.bitdrift.capture.replay.internal +import android.content.Context import io.bitdrift.capture.common.DefaultClock import io.bitdrift.capture.common.ErrorHandler import io.bitdrift.capture.common.IClock @@ -19,7 +20,8 @@ import kotlin.time.measureTimedValue internal class ReplayCaptureEngine( sessionReplayConfiguration: SessionReplayConfiguration, errorHandler: ErrorHandler, - displayManager: DisplayManagers, + context: Context, + displayManager: DisplayManagers = DisplayManagers(context), private val captureParser: ReplayParser = ReplayParser(sessionReplayConfiguration, errorHandler), private val captureFilter: ReplayFilter = ReplayFilter(), private val captureDecorations: ReplayDecorations = ReplayDecorations(errorHandler, displayManager), From c0b91c7a0e0e88e723d3de3f39f0b22891408d33 Mon Sep 17 00:00:00 2001 From: Miguel Juarez Lopez Date: Thu, 7 Nov 2024 22:31:16 -0600 Subject: [PATCH 07/12] Further simplify ReplayCaptureController --- .../capture/events/SessionReplayTarget.kt | 5 +---- .../io/bitdrift/gradletestapp/TestUtils.kt | 1 - .../capture/replay/ReplayCaptureController.kt | 20 ++++++------------- .../capture/replay/ReplayPreviewClient.kt | 11 +++------- .../replay/internal/ReplayCaptureEngine.kt | 18 ++++++++++++++++- 5 files changed, 27 insertions(+), 28 deletions(-) diff --git a/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt b/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt index 38f525ab..bada9b94 100644 --- a/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt +++ b/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt @@ -13,13 +13,12 @@ import io.bitdrift.capture.LogLevel import io.bitdrift.capture.LogType import io.bitdrift.capture.LoggerImpl import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.common.MainThreadHandler import io.bitdrift.capture.common.Runtime import io.bitdrift.capture.common.RuntimeFeature import io.bitdrift.capture.providers.toFieldValue import io.bitdrift.capture.providers.toFields -import io.bitdrift.capture.replay.ReplayLogger import io.bitdrift.capture.replay.ReplayCaptureController +import io.bitdrift.capture.replay.ReplayLogger import io.bitdrift.capture.replay.SessionReplayConfiguration import io.bitdrift.capture.replay.internal.EncodedScreenMetrics import io.bitdrift.capture.replay.internal.FilteredCapture @@ -30,7 +29,6 @@ internal class SessionReplayTarget( errorHandler: ErrorHandler, context: Context, private val logger: LoggerImpl, - mainThreadHandler: MainThreadHandler = MainThreadHandler(), ) : ISessionReplayTarget, ReplayLogger { // TODO(Augustyniak): Make non nullable and pass at initialization time after // `sessionReplayTarget` argument is moved from logger creation time to logger start time. @@ -41,7 +39,6 @@ internal class SessionReplayTarget( this, configuration, context, - mainThreadHandler, ) override fun captureScreen() { diff --git a/platform/jvm/gradle-test-app/src/androidTest/java/io/bitdrift/gradletestapp/TestUtils.kt b/platform/jvm/gradle-test-app/src/androidTest/java/io/bitdrift/gradletestapp/TestUtils.kt index 718d5e75..2b378101 100644 --- a/platform/jvm/gradle-test-app/src/androidTest/java/io/bitdrift/gradletestapp/TestUtils.kt +++ b/platform/jvm/gradle-test-app/src/androidTest/java/io/bitdrift/gradletestapp/TestUtils.kt @@ -67,7 +67,6 @@ object TestUtils { Log.e("Replay Tests", message, e) } }, - SessionReplayConfiguration(), context, ) } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt index 9112dcae..d03443c8 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt @@ -23,20 +23,16 @@ import java.util.concurrent.ScheduledExecutorService * @param runtime allows for the feature to be remotely disabled */ class ReplayCaptureController( - private val errorHandler: ErrorHandler, - private val logger: ReplayLogger, - private val sessionReplayConfiguration: SessionReplayConfiguration, - context: Context, - private val mainThreadHandler: MainThreadHandler = MainThreadHandler(), - private val executor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor { - Thread(it, "io.bitdrift.capture.session-replay") - }, + errorHandler: ErrorHandler, + logger: ReplayLogger, + sessionReplayConfiguration: SessionReplayConfiguration, + context: Context ) { private val replayCaptureEngine: ReplayCaptureEngine init { L.logger = logger - replayCaptureEngine = ReplayCaptureEngine(sessionReplayConfiguration, errorHandler, context) + replayCaptureEngine = ReplayCaptureEngine(sessionReplayConfiguration, errorHandler, context, logger = logger) } /** @@ -44,11 +40,7 @@ class ReplayCaptureController( * at initialization time. */ fun captureScreen(skipReplayComposeViews: Boolean) { - mainThreadHandler.run { - replayCaptureEngine.captureScreen(executor, skipReplayComposeViews) { byteArray, screen, metrics -> - logger.onScreenCaptured(byteArray, screen, metrics) - } - } + replayCaptureEngine.captureScreen(skipReplayComposeViews) } internal object L { diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt index 8ca5912d..9729dbf7 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt @@ -37,18 +37,15 @@ import java.util.concurrent.TimeUnit class ReplayPreviewClient( errorHandler: ErrorHandler, private val logger: ReplayLogger, - sessionReplayConfiguration: SessionReplayConfiguration, context: Context, + sessionReplayConfiguration: SessionReplayConfiguration = SessionReplayConfiguration(), protocol: String = "ws", host: String = "10.0.2.2", port: Int = 3001, ) : ReplayLogger { - private val replayCaptureEngine: ReplayCaptureEngine = ReplayCaptureEngine(sessionReplayConfiguration, errorHandler, context) + private val replayCaptureEngine: ReplayCaptureEngine = ReplayCaptureEngine(sessionReplayConfiguration, errorHandler, context, logger = logger) - private val executor: ExecutorService = Executors.newSingleThreadExecutor { - Thread(it, "io.bitdrift.capture.session-replay-client") - } // Calling this is necessary to capture the display size private val client: OkHttpClient = OkHttpClient.Builder() .readTimeout(0, TimeUnit.MILLISECONDS) @@ -75,9 +72,7 @@ class ReplayPreviewClient( * Capture the screen and send it over the websocket connection after processing */ fun captureScreen() { - replayCaptureEngine.captureScreen(executor, skipReplayComposeViews = false) { encodedScreen, screen, metrics -> - logger.onScreenCaptured(encodedScreen, screen, metrics) - } + replayCaptureEngine.captureScreen(skipReplayComposeViews = false) } /** diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt index 34a29fb2..98c655bb 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt @@ -11,9 +11,13 @@ import android.content.Context import io.bitdrift.capture.common.DefaultClock import io.bitdrift.capture.common.ErrorHandler import io.bitdrift.capture.common.IClock +import io.bitdrift.capture.common.MainThreadHandler import io.bitdrift.capture.replay.ReplayCaptureController +import io.bitdrift.capture.replay.ReplayLogger import io.bitdrift.capture.replay.SessionReplayConfiguration import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.ScheduledExecutorService import kotlin.time.measureTimedValue // This is the main logic for capturing a screen @@ -27,10 +31,22 @@ internal class ReplayCaptureEngine( private val captureDecorations: ReplayDecorations = ReplayDecorations(errorHandler, displayManager), private val replayEncoder: ReplayEncoder = ReplayEncoder(), private val clock: IClock = DefaultClock.getInstance(), + private val logger: ReplayLogger, + private val mainThreadHandler: MainThreadHandler = MainThreadHandler(), + private val executor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor { + Thread(it, "io.bitdrift.capture.session-replay") + }, ) { + fun captureScreen(skipReplayComposeViews: Boolean) { + mainThreadHandler.run { + captureScreen(skipReplayComposeViews) { byteArray, screen, metrics -> + logger.onScreenCaptured(byteArray, screen, metrics) + } + } + } + fun captureScreen( - executor: ExecutorService, skipReplayComposeViews: Boolean, completion: (encodedScreen: ByteArray, screen: FilteredCapture, metrics: EncodedScreenMetrics) -> Unit, ) { From 983caf4787877f2efc92e20e1e208325d3f2357f Mon Sep 17 00:00:00 2001 From: Miguel Juarez Lopez Date: Thu, 7 Nov 2024 22:33:58 -0600 Subject: [PATCH 08/12] Undo unnecessary changes --- platform/jvm/capture-apollo3/build.gradle.kts | 2 +- platform/jvm/capture/build.gradle.kts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/platform/jvm/capture-apollo3/build.gradle.kts b/platform/jvm/capture-apollo3/build.gradle.kts index d724ac83..c40d2ff3 100644 --- a/platform/jvm/capture-apollo3/build.gradle.kts +++ b/platform/jvm/capture-apollo3/build.gradle.kts @@ -57,7 +57,7 @@ dependencies { implementation(project(":capture")) implementation("com.apollographql.apollo3:apollo-runtime:3.8.3") - testImplementation("com.google.truth:truth:1.4.4") + testImplementation("com.google.truth:truth:1.1.4") testImplementation("junit:junit:4.13.2") testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0") // last version with Java 8 support } diff --git a/platform/jvm/capture/build.gradle.kts b/platform/jvm/capture/build.gradle.kts index 531b77b1..239d4fcf 100644 --- a/platform/jvm/capture/build.gradle.kts +++ b/platform/jvm/capture/build.gradle.kts @@ -84,7 +84,7 @@ cargo { module = "../.." targetDirectory = "../../../target" targets = listOf("arm64", "x86_64") - pythonCommand = "/opt/homebrew/bin/python3.12" + pythonCommand = "python3" } // workaround bug in rust-android-gradle plugin that causes .so to not be available on app launch From 4d673d8c2be76ad79b0cdcc91b428f2ec5210452 Mon Sep 17 00:00:00 2001 From: Miguel Juarez Lopez Date: Fri, 8 Nov 2024 18:15:33 -0600 Subject: [PATCH 09/12] fmt --- .../io/bitdrift/capture/replay/ReplayCaptureController.kt | 6 +----- .../io/bitdrift/capture/replay/ReplayPreviewClient.kt | 3 --- .../bitdrift/capture/replay/internal/ReplayCaptureEngine.kt | 1 - 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt index d03443c8..c2a15dae 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt @@ -9,11 +9,7 @@ package io.bitdrift.capture.replay import android.content.Context import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.common.MainThreadHandler -import io.bitdrift.capture.replay.internal.DisplayManagers import io.bitdrift.capture.replay.internal.ReplayCaptureEngine -import java.util.concurrent.Executors -import java.util.concurrent.ScheduledExecutorService /** * Sets up and controls the replay feature @@ -26,7 +22,7 @@ class ReplayCaptureController( errorHandler: ErrorHandler, logger: ReplayLogger, sessionReplayConfiguration: SessionReplayConfiguration, - context: Context + context: Context, ) { private val replayCaptureEngine: ReplayCaptureEngine diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt index 9729dbf7..c4211664 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt @@ -11,7 +11,6 @@ import android.content.Context import android.util.Base64 import android.util.Log import io.bitdrift.capture.common.ErrorHandler -import io.bitdrift.capture.replay.internal.DisplayManagers import io.bitdrift.capture.replay.internal.EncodedScreenMetrics import io.bitdrift.capture.replay.internal.FilteredCapture import io.bitdrift.capture.replay.internal.ReplayCaptureEngine @@ -22,8 +21,6 @@ import okhttp3.WebSocket import okhttp3.WebSocketListener import okio.ByteString import okio.ByteString.Companion.toByteString -import java.util.concurrent.ExecutorService -import java.util.concurrent.Executors import java.util.concurrent.TimeUnit /** diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt index 98c655bb..a42ce722 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt @@ -15,7 +15,6 @@ import io.bitdrift.capture.common.MainThreadHandler import io.bitdrift.capture.replay.ReplayCaptureController import io.bitdrift.capture.replay.ReplayLogger import io.bitdrift.capture.replay.SessionReplayConfiguration -import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.concurrent.ScheduledExecutorService import kotlin.time.measureTimedValue From 0d7a2d073e3518401624ee20ea61fcf0e7082a8b Mon Sep 17 00:00:00 2001 From: Miguel Juarez Lopez Date: Sat, 9 Nov 2024 12:42:23 -0600 Subject: [PATCH 10/12] fmt --- .../io/bitdrift/capture/replay/ReplayCaptureController.kt | 7 ++++++- .../io/bitdrift/capture/replay/ReplayPreviewClient.kt | 7 ++++++- .../capture/replay/internal/ReplayCaptureEngine.kt | 2 +- .../capture/replay/internal/compose/ComposeTreeParser.kt | 5 ++++- 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt index c2a15dae..01475227 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt @@ -28,7 +28,12 @@ class ReplayCaptureController( init { L.logger = logger - replayCaptureEngine = ReplayCaptureEngine(sessionReplayConfiguration, errorHandler, context, logger = logger) + replayCaptureEngine = ReplayCaptureEngine( + sessionReplayConfiguration, + errorHandler, + context, + logger, + ) } /** diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt index c4211664..64aaaf3f 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt @@ -41,7 +41,12 @@ class ReplayPreviewClient( port: Int = 3001, ) : ReplayLogger { - private val replayCaptureEngine: ReplayCaptureEngine = ReplayCaptureEngine(sessionReplayConfiguration, errorHandler, context, logger = logger) + private val replayCaptureEngine: ReplayCaptureEngine = ReplayCaptureEngine( + sessionReplayConfiguration, + errorHandler, + context, + logger, + ) // Calling this is necessary to capture the display size private val client: OkHttpClient = OkHttpClient.Builder() diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt index a42ce722..48ce40d6 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt @@ -24,13 +24,13 @@ internal class ReplayCaptureEngine( sessionReplayConfiguration: SessionReplayConfiguration, errorHandler: ErrorHandler, context: Context, + private val logger: ReplayLogger, displayManager: DisplayManagers = DisplayManagers(context), private val captureParser: ReplayParser = ReplayParser(sessionReplayConfiguration, errorHandler), private val captureFilter: ReplayFilter = ReplayFilter(), private val captureDecorations: ReplayDecorations = ReplayDecorations(errorHandler, displayManager), private val replayEncoder: ReplayEncoder = ReplayEncoder(), private val clock: IClock = DefaultClock.getInstance(), - private val logger: ReplayLogger, private val mainThreadHandler: MainThreadHandler = MainThreadHandler(), private val executor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor { Thread(it, "io.bitdrift.capture.session-replay") diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt index 2fe14526..46b045df 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/compose/ComposeTreeParser.kt @@ -34,7 +34,10 @@ internal object ComposeTreeParser { val semanticsOwner = if (androidComposeView is AndroidComposeView) { androidComposeView.semanticsOwner } else { - ReplayCaptureController.L.e(null, "View passed to ComposeTreeParser.parse() is not an AndroidComposeView. view=${androidComposeView.javaClass.name}") + ReplayCaptureController.L.e( + null, + "View passed to ComposeTreeParser.parse() is not an AndroidComposeView. view=${androidComposeView.javaClass.name}", + ) return ScannableView.IgnoredComposeView } val rootNode = semanticsOwner.unmergedRootSemanticsNode From bb95bff82ec77474b5b34e8baad23097c7b9c96f Mon Sep 17 00:00:00 2001 From: Miguel Juarez Lopez Date: Sat, 9 Nov 2024 17:34:29 -0600 Subject: [PATCH 11/12] Fix and refactor tests --- .../bitdrift/capture/events/SessionReplayTarget.kt | 3 +++ .../io/bitdrift/capture/SessionReplayTargetTest.kt | 12 ++++++------ .../capture/replay/ReplayCaptureController.kt | 3 +++ .../bitdrift/capture/replay/ReplayPreviewClient.kt | 2 ++ .../capture/replay/internal/ReplayCaptureEngine.kt | 2 +- 5 files changed, 15 insertions(+), 7 deletions(-) diff --git a/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt b/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt index bada9b94..740699a8 100644 --- a/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt +++ b/platform/jvm/capture/src/main/kotlin/io/bitdrift/capture/events/SessionReplayTarget.kt @@ -13,6 +13,7 @@ import io.bitdrift.capture.LogLevel import io.bitdrift.capture.LogType import io.bitdrift.capture.LoggerImpl import io.bitdrift.capture.common.ErrorHandler +import io.bitdrift.capture.common.MainThreadHandler import io.bitdrift.capture.common.Runtime import io.bitdrift.capture.common.RuntimeFeature import io.bitdrift.capture.providers.toFieldValue @@ -29,6 +30,7 @@ internal class SessionReplayTarget( errorHandler: ErrorHandler, context: Context, private val logger: LoggerImpl, + mainThreadHandler: MainThreadHandler = MainThreadHandler(), ) : ISessionReplayTarget, ReplayLogger { // TODO(Augustyniak): Make non nullable and pass at initialization time after // `sessionReplayTarget` argument is moved from logger creation time to logger start time. @@ -39,6 +41,7 @@ internal class SessionReplayTarget( this, configuration, context, + mainThreadHandler, ) override fun captureScreen() { diff --git a/platform/jvm/capture/src/test/kotlin/io/bitdrift/capture/SessionReplayTargetTest.kt b/platform/jvm/capture/src/test/kotlin/io/bitdrift/capture/SessionReplayTargetTest.kt index 328dbd0b..8b667acd 100644 --- a/platform/jvm/capture/src/test/kotlin/io/bitdrift/capture/SessionReplayTargetTest.kt +++ b/platform/jvm/capture/src/test/kotlin/io/bitdrift/capture/SessionReplayTargetTest.kt @@ -26,11 +26,11 @@ class SessionReplayTargetTest { private val handler: MainThreadHandler = Mocks.sameThreadHandler private val target = SessionReplayTarget( - errorHandler = errorHandler, - context = ApplicationProvider.getApplicationContext(), - logger = logger, - configuration = SessionReplayConfiguration(), - mainThreadHandler = handler, + SessionReplayConfiguration(), + errorHandler, + ApplicationProvider.getApplicationContext(), + logger, + handler, ) init { @@ -46,7 +46,7 @@ class SessionReplayTargetTest { fun sessionReplayTargetEmitsScreenLog() { target.captureScreen() // TODO: Make this test work, the issue is that in test environment session replay - // sees 0 views and as a result it doesn't emit a session replay screen log. + // sees 0 views and as a result it doesn't emit a session replay screen log. // verify(logger, timeout(1000).times(1)).logSessionReplayScreen(any(), any()) } } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt index 01475227..b8b0aa42 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayCaptureController.kt @@ -9,6 +9,7 @@ package io.bitdrift.capture.replay import android.content.Context import io.bitdrift.capture.common.ErrorHandler +import io.bitdrift.capture.common.MainThreadHandler import io.bitdrift.capture.replay.internal.ReplayCaptureEngine /** @@ -23,6 +24,7 @@ class ReplayCaptureController( logger: ReplayLogger, sessionReplayConfiguration: SessionReplayConfiguration, context: Context, + mainThreadHandler: MainThreadHandler, ) { private val replayCaptureEngine: ReplayCaptureEngine @@ -33,6 +35,7 @@ class ReplayCaptureController( errorHandler, context, logger, + mainThreadHandler, ) } diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt index 64aaaf3f..f69c5f2e 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/ReplayPreviewClient.kt @@ -11,6 +11,7 @@ import android.content.Context import android.util.Base64 import android.util.Log import io.bitdrift.capture.common.ErrorHandler +import io.bitdrift.capture.common.MainThreadHandler import io.bitdrift.capture.replay.internal.EncodedScreenMetrics import io.bitdrift.capture.replay.internal.FilteredCapture import io.bitdrift.capture.replay.internal.ReplayCaptureEngine @@ -46,6 +47,7 @@ class ReplayPreviewClient( errorHandler, context, logger, + MainThreadHandler(), ) // Calling this is necessary to capture the display size diff --git a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt index 48ce40d6..bc8a4e11 100644 --- a/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt +++ b/platform/jvm/replay/src/main/kotlin/io/bitdrift/capture/replay/internal/ReplayCaptureEngine.kt @@ -25,13 +25,13 @@ internal class ReplayCaptureEngine( errorHandler: ErrorHandler, context: Context, private val logger: ReplayLogger, + private val mainThreadHandler: MainThreadHandler, displayManager: DisplayManagers = DisplayManagers(context), private val captureParser: ReplayParser = ReplayParser(sessionReplayConfiguration, errorHandler), private val captureFilter: ReplayFilter = ReplayFilter(), private val captureDecorations: ReplayDecorations = ReplayDecorations(errorHandler, displayManager), private val replayEncoder: ReplayEncoder = ReplayEncoder(), private val clock: IClock = DefaultClock.getInstance(), - private val mainThreadHandler: MainThreadHandler = MainThreadHandler(), private val executor: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor { Thread(it, "io.bitdrift.capture.session-replay") }, From db8eff6ec3d3123157f7d2162db455369f806a21 Mon Sep 17 00:00:00 2001 From: Miguel Juarez Lopez Date: Sun, 10 Nov 2024 08:37:01 -0600 Subject: [PATCH 12/12] fix main activity --- examples/android/MainActivity.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/examples/android/MainActivity.kt b/examples/android/MainActivity.kt index df0d0cab..825972eb 100644 --- a/examples/android/MainActivity.kt +++ b/examples/android/MainActivity.kt @@ -33,7 +33,6 @@ import io.bitdrift.capture.LogLevel import io.bitdrift.capture.common.ErrorHandler import io.bitdrift.capture.network.okhttp.CaptureOkHttpEventListenerFactory import io.bitdrift.capture.replay.ReplayLogger -import io.bitdrift.capture.replay.ReplayModule import io.bitdrift.capture.replay.ReplayPreviewClient import io.bitdrift.capture.replay.SessionReplayConfiguration import io.bitdrift.capture.replay.internal.EncodedScreenMetrics @@ -81,8 +80,8 @@ class MainActivity : ComponentActivity() { Log.e("HelloWorldApp", message, e) } }, + this.applicationContext, SessionReplayConfiguration(), - this.applicationContext ) } private lateinit var clipboardManager: ClipboardManager