Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshots.SnapshotStateList
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
Expand Down Expand Up @@ -313,16 +315,47 @@ private fun Nav2ComposeHomeRoute(routeSpec: Nav2RouteSpec, onBrowseProducts: ()
Nav2ComposeActionRoute(routeSpec, buttons = listOf("Browse Products" to onBrowseProducts))
}

@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun Nav2ComposeProductListRoute(
routeSpec: Nav2RouteSpec,
onOpenProduct42: () -> Unit,
onOpenProduct7: () -> Unit,
) {
Nav2ComposeActionRoute(
routeSpec,
buttons = listOf("Open Product 42" to onOpenProduct42, "Open Product 7" to onOpenProduct7),
)
var showProductItems by rememberSaveable { mutableStateOf(false) }

Nav2ComposeRouteScaffold(
routeSpec = routeSpec,
cardContent = {
SentryTraced(
tag = "product_list_actions",
modifier = Modifier.fillMaxWidth(),
enableUserInteractionTracing = false,
) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Nav2ComposeRouteButton("Open Product 42", onOpenProduct42)
Nav2ComposeRouteButton("Open Product 7", onOpenProduct7)
}
}
},
) {
Nav2ComposeRouteButton(
label = if (showProductItems) "Hide Product Items" else "Show Product Items",
onClick = { showProductItems = !showProductItems },
)

if (showProductItems) {
SentryTraced(
tag = "product_list_items",
modifier = Modifier.fillMaxWidth(),
enableUserInteractionTracing = false,
) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
repeat(PRODUCT_LIST_ITEM_COUNT) { index -> Nav2ComposeProductListItem(index + 1) }
}
}
}
}
}

@Composable
Expand Down Expand Up @@ -388,12 +421,15 @@ private fun Nav2ComposeActionRoute(
arguments: Map<String, Any?> = emptyMap(),
buttons: List<Pair<String, () -> Unit>>,
) {
Nav2ComposeRouteScaffold(routeSpec) {
routeSpec.displayArguments(arguments).forEach { (label, value) ->
Nav2ComposeRouteInfo(label, value)
}
buttons.forEach { (label, onClick) -> Nav2ComposeRouteButton(label, onClick) }
}
Nav2ComposeRouteScaffold(
routeSpec = routeSpec,
cardContent = {
routeSpec.displayArguments(arguments).forEach { (label, value) ->
Nav2ComposeRouteInfo(label, value)
}
buttons.forEach { (label, onClick) -> Nav2ComposeRouteButton(label, onClick) }
},
)
}

@Composable
Expand Down Expand Up @@ -489,6 +525,7 @@ private fun Nav2ComposeShareSheetRoute(
@Composable
private fun Nav2ComposeRouteScaffold(
routeSpec: Nav2RouteSpec,
cardContent: (@Composable ColumnScope.() -> Unit)? = null,
content: (@Composable ColumnScope.() -> Unit)? = null,
) {
Column(
Expand All @@ -501,7 +538,7 @@ private fun Nav2ComposeRouteScaffold(
fontWeight = FontWeight.Bold,
)
routeSpec.description?.let { Text(it, style = MaterialTheme.typography.bodyMedium) }
if (content != null) {
if (cardContent != null) {
Card(
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant),
modifier = Modifier.fillMaxWidth(),
Expand All @@ -510,10 +547,34 @@ private fun Nav2ComposeRouteScaffold(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
) {
content()
cardContent()
}
}
}
Comment thread
sentry[bot] marked this conversation as resolved.
content?.invoke(this)
Comment thread
cursor[bot] marked this conversation as resolved.
}
}

@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun Nav2ComposeProductListItem(index: Int) {
SentryTraced(
tag = "product_list_item_$index",
modifier = Modifier.fillMaxWidth(),
enableUserInteractionTracing = false,
) {
Card(
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant),
modifier = Modifier.fillMaxWidth(),
) {
Row(
modifier = Modifier.fillMaxWidth().padding(12.dp),
horizontalArrangement = Arrangement.SpaceBetween,
) {
Text("Product #$index", fontWeight = FontWeight.Bold)
Text("SKU-$index")
}
}
}
}

Expand All @@ -534,6 +595,8 @@ private fun Nav2ComposeRouteButton(

private fun nav2ComposeInteractionTag(label: String): String = "Nav2 Compose $label"

private const val PRODUCT_LIST_ITEM_COUNT = 20

@Composable
private fun Nav2ComposeRouteInfo(label: String, value: String) {
Row(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,14 @@ import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.LinearLayout
import android.widget.ScrollView
import android.widget.TextView
import androidx.core.view.doOnPreDraw
import androidx.core.view.setPadding
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import io.sentry.ISpan
import io.sentry.Sentry
import io.sentry.samples.android.R
import kotlinx.coroutines.launch

Expand Down Expand Up @@ -64,6 +68,7 @@ class Nav2RouteFragment : Fragment() {
activity.navigateTo(Nav2Destination.ProductDetail("7", "product-list"))
},
),
trailingContent = { addProductListItemsToggle() },
)

Nav2RouteNames.PRODUCT_DETAIL -> productDetailLayout(activity)
Expand Down Expand Up @@ -163,6 +168,7 @@ class Nav2RouteFragment : Fragment() {
routeSpec: Nav2RouteSpec,
arguments: Bundle? = null,
buttons: List<RouteButton> = emptyList(),
trailingContent: (LinearLayout.() -> Unit)? = null,
): View {
val info = routeSpec.displayArguments(arguments)
return LinearLayout(requireContext()).apply {
Expand All @@ -182,9 +188,121 @@ class Nav2RouteFragment : Fragment() {
}
)
}
trailingContent?.invoke(this)
}
}

private fun LinearLayout.addProductListItemsToggle() {
var itemsCreated = false
var itemsVisible = false
val listContainer =
LinearLayout(context).apply {
orientation = LinearLayout.VERTICAL
setPadding(0, 8.dp, 0, 0)
}
val scrollView =
ScrollView(context).apply {
visibility = View.GONE
layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, 280.dp)
addView(listContainer, ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT))
}

addView(
Button(context).apply {
text = "Show Product Items"
isAllCaps = false
setOnClickListener {
itemsVisible = !itemsVisible
text = if (itemsVisible) "Hide Product Items" else "Show Product Items"
if (itemsVisible && !itemsCreated) {
populateProductListItems(listContainer)
itemsCreated = true
}
scrollView.visibility = if (itemsVisible) View.VISIBLE else View.GONE
}
layoutParams = LinearLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
}
)
addView(scrollView)
}

private fun populateProductListItems(listContainer: LinearLayout) {
val ownerSpan = Sentry.getSpan()
val compositionParent =
ownerSpan?.startChild(
OP_PARENT_COMPOSITION,
"Fragment Product List Item List Composition",
)
try {
recordManualUiSpan(compositionParent, OP_COMPOSE, "fragment_product_list_items") {
repeat(PRODUCT_LIST_ITEM_COUNT) { index ->
val itemNumber = index + 1
recordManualUiSpan(
compositionParent,
OP_COMPOSE,
"fragment_product_list_item_$itemNumber",
) {
listContainer.addView(productListItemRow(itemNumber))
}
}
}
} finally {
compositionParent?.finish()
}

listContainer.doOnPreDraw {
val renderParent =
ownerSpan?.startChild(
OP_PARENT_RENDER,
"Fragment Product List Item List Render",
)
try {
recordManualUiSpan(renderParent, OP_RENDER, "fragment_product_list_items")
repeat(PRODUCT_LIST_ITEM_COUNT) { index ->
recordManualUiSpan(renderParent, OP_RENDER, "fragment_product_list_item_${index + 1}")
}
} finally {
renderParent?.finish()
}
}
}

private fun recordManualUiSpan(
parentSpan: ISpan?,
operation: String,
description: String,
block: () -> Unit = {},
) {
val span = parentSpan?.startChild(operation, description)
span?.setData("sample.nav2_fragment_manual_ui_span", true)
try {
block()
} finally {
span?.finish()
}
}

private fun productListItemRow(itemNumber: Int): View =
LinearLayout(requireContext()).apply {
orientation = LinearLayout.HORIZONTAL
setPadding(12.dp)
setBackgroundColor(color(android.R.color.white))
addView(
TextView(context).apply {
text = "Product #$itemNumber"
textSize = 14f
setTypeface(null, Typeface.BOLD)
layoutParams = LinearLayout.LayoutParams(0, WRAP_CONTENT, 1f)
}
)
addView(
TextView(context).apply {
text = "SKU-$itemNumber"
textSize = 14f
}
)
}

private fun titleText(textValue: String): TextView =
TextView(requireContext()).apply {
text = textValue
Expand Down Expand Up @@ -224,4 +342,12 @@ class Nav2RouteFragment : Fragment() {
get() = (this * resources.displayMetrics.density).toInt()

private fun color(id: Int): Int = requireContext().getColor(id)

private companion object {
private const val PRODUCT_LIST_ITEM_COUNT = 20
private const val OP_PARENT_COMPOSITION = "ui.compose.composition"
private const val OP_COMPOSE = "ui.compose"
private const val OP_PARENT_RENDER = "ui.compose.rendering"
private const val OP_RENDER = "ui.render"
}
}
Loading