Skip to content

Commit

Permalink
[andr][replay] Handle hybrid AndroidViews inside Compose (#126)
Browse files Browse the repository at this point in the history
* Handle hybrid AndroidViews inside Compose

* Add regression test
  • Loading branch information
murki authored Nov 18, 2024
1 parent f6edcc1 commit 12b60ff
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package io.bitdrift.gradletestapp

import android.view.ViewGroup
import android.widget.Button
import android.widget.TextView
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box
Expand Down Expand Up @@ -324,9 +325,9 @@ class ComposeReplayTest {
Column {
AndroidView(::TextView) {
it.layoutParams = ViewGroup.LayoutParams(200, 80)
it.text = "Baguette Avec Fromage"
it.text = "hi"
}
AndroidView(::TextView) {
AndroidView(::Button) {
it.layoutParams = ViewGroup.LayoutParams(200, 80)
it.text = "short"
}
Expand All @@ -337,12 +338,10 @@ class ComposeReplayTest {
val capture = verifyReplayScreen(viewCount = 10)
// Column
assertThat(capture).contains(ReplayRect(ReplayType.View, 0, 88, 200, 160))
// AndroidView Top
// TODO(murki): The ReplayRect should be a Label
assertThat(capture).contains(ReplayRect(ReplayType.View, 0, 88, 200, 80))
// AndroidView Bottom
// TODO(murki): The ReplayRect should be a Label
assertThat(capture).contains(ReplayRect(ReplayType.View, 0, 168, 200, 80))
// AndroidView w/TextView Top (width reflects the text length
assertThat(capture).contains(ReplayRect(ReplayType.Label, 3, 88, 33, 37))
// AndroidView w/Button Bottom
assertThat(capture).contains(ReplayRect(ReplayType.Button, 0, 168, 200, 80))
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ internal sealed class ScannableView {
/** The children of this view. */
abstract val children: Sequence<ScannableView>

class AndroidView(val view: View, private val skipReplayComposeViews: Boolean) : ScannableView() {
class AndroidView(val view: View, skipReplayComposeViews: Boolean) : ScannableView() {
override val displayName: String get() = view::class.java.simpleName
override val children: Sequence<ScannableView> =
view.scannableChildren(skipReplayComposeViews)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package io.bitdrift.capture.replay.internal.compose

import android.view.View
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.InternalComposeUiApi
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.platform.AndroidComposeView
import androidx.compose.ui.semantics.Role
Expand All @@ -25,6 +26,7 @@ import io.bitdrift.capture.replay.ReplayType
import io.bitdrift.capture.replay.SessionReplayController
import io.bitdrift.capture.replay.internal.ReplayRect
import io.bitdrift.capture.replay.internal.ScannableView
import io.bitdrift.capture.replay.internal.compose.ComposeTreeParser.unclippedGlobalBounds

internal object ComposeTreeParser {
internal val View.mightBeComposeView: Boolean
Expand All @@ -45,8 +47,8 @@ internal object ComposeTreeParser {
return rootNode.toScannableView()
}

@OptIn(ExperimentalComposeUiApi::class)
private fun SemanticsNode.toScannableView(): ScannableView.ComposeView {
@OptIn(ExperimentalComposeUiApi::class, InternalComposeUiApi::class)
private fun SemanticsNode.toScannableView(): ScannableView {
val notAttachedOrPlaced = !this.layoutNode.isPlaced || !this.layoutNode.isAttached
val isVisible = !this.isTransparent && !unmergedConfig.contains(SemanticsProperties.InvisibleToUser)
val type = if (notAttachedOrPlaced) {
Expand All @@ -56,6 +58,16 @@ internal object ComposeTreeParser {
} else {
this.unmergedConfig.toReplayType()
}

// Handle hybrid interop AndroidViews inside Compose elements
val interopAndroidView = this.layoutNode.getInteropView()
if (type == ReplayType.View && interopAndroidView != null) {
return ScannableView.AndroidView(
view = interopAndroidView,
skipReplayComposeViews = false,
)
}

return ScannableView.ComposeView(
replayRect = ReplayRect(
type = type,
Expand Down

0 comments on commit 12b60ff

Please sign in to comment.