From b396f276ec0f3d80e784eda7c25801b4d6a59f13 Mon Sep 17 00:00:00 2001 From: Magda Wojtowicz Date: Thu, 12 Sep 2024 14:40:17 +0200 Subject: [PATCH 01/11] created a confirmation of placed order screen --- .../demo/shop/ui/AstronomyShopActivity.kt | 12 +- .../demo/shop/ui/cart/CheckoutConfirmation.kt | 147 ++++++++---------- .../android/demo/shop/ui/cart/CheckoutInfo.kt | 36 ++--- 3 files changed, 82 insertions(+), 113 deletions(-) diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/AstronomyShopActivity.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/AstronomyShopActivity.kt index 66be7a043..c1ba4d516 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/AstronomyShopActivity.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/AstronomyShopActivity.kt @@ -32,7 +32,6 @@ import io.opentelemetry.android.demo.shop.ui.products.ProductList import io.opentelemetry.android.demo.shop.ui.cart.CartViewModel import androidx.lifecycle.viewmodel.compose.viewModel import io.opentelemetry.android.demo.shop.ui.cart.CheckoutConfirmationScreen -import io.opentelemetry.android.demo.shop.ui.cart.CheckoutInfoViewModel import io.opentelemetry.android.demo.shop.ui.cart.InfoScreen class AstronomyShopActivity : AppCompatActivity() { @@ -51,8 +50,6 @@ fun AstronomyShopScreen() { val context = LocalContext.current val astronomyShopNavController = rememberAstronomyShopNavController() val cartViewModel: CartViewModel = viewModel() - val checkoutInfoViewModel: CheckoutInfoViewModel = viewModel() - DemoAppTheme { Surface( modifier = Modifier.fillMaxSize(), @@ -108,15 +105,10 @@ fun AstronomyShopScreen() { composable(MainDestinations.CHECKOUT_INFO_ROUTE) { InfoScreen( onPlaceOrderClick = {astronomyShopNavController.navigateToCheckoutConfirmation()}, - upPress = {astronomyShopNavController.upPress()}, - checkoutInfoViewModel = checkoutInfoViewModel - ) + upPress = {astronomyShopNavController.upPress()}) } composable(MainDestinations.CHECKOUT_CONFIRMATION_ROUTE){ - CheckoutConfirmationScreen( - cartViewModel = cartViewModel, - checkoutInfoViewModel = checkoutInfoViewModel - ) + CheckoutConfirmationScreen(cartViewModel = cartViewModel) } } } diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt index 809470aef..a58a38a1d 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt @@ -5,38 +5,19 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalLifecycleOwner -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import androidx.compose.ui.Alignment +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.sp -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleEventObserver import io.opentelemetry.android.demo.shop.ui.products.ProductCard -import java.util.Locale @Composable fun CheckoutConfirmationScreen( cartViewModel: CartViewModel, - checkoutInfoViewModel: CheckoutInfoViewModel +// shippingInfo: ShippingInfo ) { - val lifecycleOwner = LocalLifecycleOwner.current - - DisposableEffect(lifecycleOwner) { - val observer = LifecycleEventObserver { _, event -> - if (event == Lifecycle.Event.ON_PAUSE || event == Lifecycle.Event.ON_STOP) { - cartViewModel.clearCart() - } - } - lifecycleOwner.lifecycle.addObserver(observer) - onDispose { - lifecycleOwner.lifecycle.removeObserver(observer) - } - } - - val shippingInfo = checkoutInfoViewModel.shippingInfo Column( modifier = Modifier @@ -44,9 +25,10 @@ fun CheckoutConfirmationScreen( .padding(16.dp) .verticalScroll(rememberScrollState()) ) { + Text( text = "Your order is complete!", - fontSize = 24.sp, + style = MaterialTheme.typography.titleLarge.copy(fontSize = 24.sp), textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() @@ -54,63 +36,39 @@ fun CheckoutConfirmationScreen( ) Text( - text = "We've sent a confirmation email to ${shippingInfo.email}.", - fontSize = 18.sp, + text = "We've sent you a confirmation email.", + style = MaterialTheme.typography.bodyMedium, textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() .padding(bottom = 32.dp) ) - val cartItems = cartViewModel.cartItems.collectAsState().value - cartItems.forEach { cartItem -> - Row( + cartViewModel.cartItems.collectAsState().value.firstOrNull()?.let { cartItem -> + ProductCard( + product = cartItem.product, + onProductClick = {}, + isNarrow = true, modifier = Modifier - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - ProductCard( - product = cartItem.product, - onProductClick = {}, - modifier = Modifier - .width(300.dp) - .height(170.dp), - isNarrow = true - ) - Column( - modifier = Modifier - .fillMaxHeight(), - verticalArrangement = Arrangement.Center - ) { - Text( - text = "Quantity: ${cartItem.quantity}", - fontSize = 12.sp, - modifier = Modifier - .padding(horizontal = 8.dp) - ) + .fillMaxWidth() + .padding(bottom = 16.dp) + ) - Text( - text = "Total: \$${String.format(Locale.US, "%.2f", cartItem.totalPrice())}", - fontSize = 14.sp, - modifier = Modifier - .padding(8.dp), - ) - } - } + Text( + text = "Total: $${cartViewModel.getTotalPrice()}", + style = MaterialTheme.typography.bodyLarge, + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + textAlign = TextAlign.End + ) } - Text( - text = "Total Price: \$${String.format(Locale.US, "%.2f", cartViewModel.getTotalPrice())}", - modifier = Modifier - .fillMaxWidth() - .padding(top = 16.dp, bottom = 16.dp), - textAlign = TextAlign.End - ) - Card( modifier = Modifier .fillMaxWidth() .padding(vertical = 16.dp), + colors = CardDefaults.cardColors(containerColor = Color.LightGray) ) { Column( modifier = Modifier.padding(16.dp), @@ -118,24 +76,45 @@ fun CheckoutConfirmationScreen( ) { Text( text = "Shipping Data", - fontWeight = FontWeight.Bold - ) - Text( - text = "Street: ${shippingInfo.streetAddress}", - ) - Text( - text = "City: ${shippingInfo.city}", - ) - Text( - text = "State: ${shippingInfo.state}", - ) - Text( - text = "Zip Code: ${shippingInfo.zipCode}", - ) - Text( - text = "Country: ${shippingInfo.country}", + style = MaterialTheme.typography.bodyLarge, + color = Color.Black ) +// Text( +// text = "Street: ${shippingInfo.streetAddress}", +// style = MaterialTheme.typography.bodyMedium, +// color = Color.Black +// ) +// Text( +// text = "City: ${shippingInfo.city}", +// style = MaterialTheme.typography.bodyMedium, +// color = Color.Black +// ) +// Text( +// text = "State: ${shippingInfo.state}", +// style = MaterialTheme.typography.bodyMedium, +// color = Color.Black +// ) +// Text( +// text = "Zip Code: ${shippingInfo.zipCode}", +// style = MaterialTheme.typography.bodyMedium, +// color = Color.Black +// ) +// Text( +// text = "Country: ${shippingInfo.country}", +// style = MaterialTheme.typography.bodyMedium, +// color = Color.Black +// ) } } + + Button( + onClick = { /* TODO Handle continue shopping action */ }, + modifier = Modifier + .fillMaxWidth() + .height(50.dp) + .align(Alignment.CenterHorizontally) + ) { + Text(text = "Continue Shopping") + } } -} +} \ No newline at end of file diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt index d8365bf39..e4218f568 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt @@ -74,20 +74,20 @@ fun InfoFieldsSection( @Composable fun InfoScreen( onPlaceOrderClick: () -> Unit, - upPress: () -> Unit, - checkoutInfoViewModel: CheckoutInfoViewModel + upPress: () -> Unit ) { - val shippingInfo = checkoutInfoViewModel.shippingInfo - val paymentInfo = checkoutInfoViewModel.paymentInfo + var shippingInfo by remember { mutableStateOf(ShippingInfo()) } + var paymentInfo by remember { mutableStateOf(PaymentInfo()) } val focusManager = LocalFocusManager.current - val canProceed = checkoutInfoViewModel.canProceedToCheckout() + val canProceed = shippingInfo.isComplete() && paymentInfo.isComplete() Box( modifier = Modifier .fillMaxSize() .background(Color.White) ) { + Column( modifier = Modifier .fillMaxSize() @@ -99,12 +99,12 @@ fun InfoScreen( InfoFieldsSection( fields = listOf( - Triple("E-mail Address", shippingInfo.email) { checkoutInfoViewModel.updateShippingInfo(shippingInfo.copy(email = it)) }, - Triple("Street Address", shippingInfo.streetAddress) { checkoutInfoViewModel.updateShippingInfo(shippingInfo.copy(streetAddress = it)) }, - Triple("Zip Code", shippingInfo.zipCode) { checkoutInfoViewModel.updateShippingInfo(shippingInfo.copy(zipCode = it)) }, - Triple("City", shippingInfo.city) { checkoutInfoViewModel.updateShippingInfo(shippingInfo.copy(city = it)) }, - Triple("State", shippingInfo.state) { checkoutInfoViewModel.updateShippingInfo(shippingInfo.copy(state = it)) }, - Triple("Country", shippingInfo.country) { checkoutInfoViewModel.updateShippingInfo(shippingInfo.copy(country = it)) } + Triple("E-mail Address", shippingInfo.email) { shippingInfo = shippingInfo.copy(email = it) }, + Triple("Street Address", shippingInfo.streetAddress) { shippingInfo = shippingInfo.copy(streetAddress = it) }, + Triple("Zip Code", shippingInfo.zipCode) { shippingInfo = shippingInfo.copy(zipCode = it) }, + Triple("City", shippingInfo.city) { shippingInfo = shippingInfo.copy(city = it) }, + Triple("State", shippingInfo.state) { shippingInfo = shippingInfo.copy(state = it) }, + Triple("Country", shippingInfo.country) { shippingInfo = shippingInfo.copy(country = it) } ) ) @@ -114,19 +114,17 @@ fun InfoScreen( InfoFieldsSection( fields = listOf( - Triple("Credit Card Number", paymentInfo.creditCardNumber) { checkoutInfoViewModel.updatePaymentInfo(paymentInfo.copy(creditCardNumber = it)) }, - Triple("Month", paymentInfo.expiryMonth) { checkoutInfoViewModel.updatePaymentInfo(paymentInfo.copy(expiryMonth = it)) }, - Triple("Year", paymentInfo.expiryYear) { checkoutInfoViewModel.updatePaymentInfo(paymentInfo.copy(expiryYear = it)) }, - Triple("CVV", paymentInfo.cvv) { checkoutInfoViewModel.updatePaymentInfo(paymentInfo.copy(cvv = it)) } + Triple("Credit Card Number", paymentInfo.creditCardNumber) { paymentInfo = paymentInfo.copy(creditCardNumber = it) }, + Triple("Month", paymentInfo.expiryMonth) { paymentInfo = paymentInfo.copy(expiryMonth = it) }, + Triple("Year", paymentInfo.expiryYear) { paymentInfo = paymentInfo.copy(expiryYear = it) }, + Triple("CVV", paymentInfo.cvv) { paymentInfo = paymentInfo.copy(cvv = it) } ) ) Spacer(modifier = Modifier.height(16.dp)) Button( - onClick = { - onPlaceOrderClick() - }, + onClick = onPlaceOrderClick, modifier = Modifier.fillMaxWidth(), enabled = canProceed ) { @@ -141,4 +139,4 @@ fun InfoScreen( .padding(8.dp) ) } -} \ No newline at end of file +} From 998f395033e039594e8315a9a4886a02f201a4cd Mon Sep 17 00:00:00 2001 From: Magda Wojtowicz Date: Thu, 12 Sep 2024 14:46:59 +0200 Subject: [PATCH 02/11] moved checkout info to a ViewModel --- .../android/demo/shop/ui/cart/CheckoutInfo.kt | 38 ++++++++++--------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt index e4218f568..305a1c165 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.unit.dp import io.opentelemetry.android.demo.shop.ui.components.UpPressButton import androidx.compose.ui.Alignment import androidx.compose.ui.text.style.TextAlign +import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun InfoField( @@ -74,20 +75,20 @@ fun InfoFieldsSection( @Composable fun InfoScreen( onPlaceOrderClick: () -> Unit, - upPress: () -> Unit + upPress: () -> Unit, + checkoutInfoViewModel: CheckoutInfoViewModel = viewModel() ) { - var shippingInfo by remember { mutableStateOf(ShippingInfo()) } - var paymentInfo by remember { mutableStateOf(PaymentInfo()) } + val shippingInfo by remember { mutableStateOf(checkoutInfoViewModel.shippingInfo) } + val paymentInfo by remember { mutableStateOf(checkoutInfoViewModel.paymentInfo) } val focusManager = LocalFocusManager.current - val canProceed = shippingInfo.isComplete() && paymentInfo.isComplete() + val canProceed = checkoutInfoViewModel.canProceedToCheckout() Box( modifier = Modifier .fillMaxSize() .background(Color.White) ) { - Column( modifier = Modifier .fillMaxSize() @@ -99,12 +100,12 @@ fun InfoScreen( InfoFieldsSection( fields = listOf( - Triple("E-mail Address", shippingInfo.email) { shippingInfo = shippingInfo.copy(email = it) }, - Triple("Street Address", shippingInfo.streetAddress) { shippingInfo = shippingInfo.copy(streetAddress = it) }, - Triple("Zip Code", shippingInfo.zipCode) { shippingInfo = shippingInfo.copy(zipCode = it) }, - Triple("City", shippingInfo.city) { shippingInfo = shippingInfo.copy(city = it) }, - Triple("State", shippingInfo.state) { shippingInfo = shippingInfo.copy(state = it) }, - Triple("Country", shippingInfo.country) { shippingInfo = shippingInfo.copy(country = it) } + Triple("E-mail Address", shippingInfo.email) { checkoutInfoViewModel.updateShippingInfo(shippingInfo.copy(email = it)) }, + Triple("Street Address", shippingInfo.streetAddress) { checkoutInfoViewModel.updateShippingInfo(shippingInfo.copy(streetAddress = it)) }, + Triple("Zip Code", shippingInfo.zipCode) { checkoutInfoViewModel.updateShippingInfo(shippingInfo.copy(zipCode = it)) }, + Triple("City", shippingInfo.city) { checkoutInfoViewModel.updateShippingInfo(shippingInfo.copy(city = it)) }, + Triple("State", shippingInfo.state) { checkoutInfoViewModel.updateShippingInfo(shippingInfo.copy(state = it)) }, + Triple("Country", shippingInfo.country) { checkoutInfoViewModel.updateShippingInfo(shippingInfo.copy(country = it)) } ) ) @@ -114,17 +115,20 @@ fun InfoScreen( InfoFieldsSection( fields = listOf( - Triple("Credit Card Number", paymentInfo.creditCardNumber) { paymentInfo = paymentInfo.copy(creditCardNumber = it) }, - Triple("Month", paymentInfo.expiryMonth) { paymentInfo = paymentInfo.copy(expiryMonth = it) }, - Triple("Year", paymentInfo.expiryYear) { paymentInfo = paymentInfo.copy(expiryYear = it) }, - Triple("CVV", paymentInfo.cvv) { paymentInfo = paymentInfo.copy(cvv = it) } + Triple("Credit Card Number", paymentInfo.creditCardNumber) { checkoutInfoViewModel.updatePaymentInfo(paymentInfo.copy(creditCardNumber = it)) }, + Triple("Month", paymentInfo.expiryMonth) { checkoutInfoViewModel.updatePaymentInfo(paymentInfo.copy(expiryMonth = it)) }, + Triple("Year", paymentInfo.expiryYear) { checkoutInfoViewModel.updatePaymentInfo(paymentInfo.copy(expiryYear = it)) }, + Triple("CVV", paymentInfo.cvv) { checkoutInfoViewModel.updatePaymentInfo(paymentInfo.copy(cvv = it)) } ) ) Spacer(modifier = Modifier.height(16.dp)) Button( - onClick = onPlaceOrderClick, + onClick = { + // Proceed to the next screen when clicked + onPlaceOrderClick() + }, modifier = Modifier.fillMaxWidth(), enabled = canProceed ) { @@ -139,4 +143,4 @@ fun InfoScreen( .padding(8.dp) ) } -} +} \ No newline at end of file From c8070cc7ef554149831375431161ae3fca37b2f3 Mon Sep 17 00:00:00 2001 From: Magda Wojtowicz Date: Thu, 12 Sep 2024 16:20:40 +0200 Subject: [PATCH 03/11] passed shipping info to checkout confirmation --- .../demo/shop/ui/AstronomyShopActivity.kt | 12 ++++- .../demo/shop/ui/cart/CheckoutConfirmation.kt | 53 ++++++++++--------- .../android/demo/shop/ui/cart/CheckoutInfo.kt | 2 +- 3 files changed, 38 insertions(+), 29 deletions(-) diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/AstronomyShopActivity.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/AstronomyShopActivity.kt index c1ba4d516..66be7a043 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/AstronomyShopActivity.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/AstronomyShopActivity.kt @@ -32,6 +32,7 @@ import io.opentelemetry.android.demo.shop.ui.products.ProductList import io.opentelemetry.android.demo.shop.ui.cart.CartViewModel import androidx.lifecycle.viewmodel.compose.viewModel import io.opentelemetry.android.demo.shop.ui.cart.CheckoutConfirmationScreen +import io.opentelemetry.android.demo.shop.ui.cart.CheckoutInfoViewModel import io.opentelemetry.android.demo.shop.ui.cart.InfoScreen class AstronomyShopActivity : AppCompatActivity() { @@ -50,6 +51,8 @@ fun AstronomyShopScreen() { val context = LocalContext.current val astronomyShopNavController = rememberAstronomyShopNavController() val cartViewModel: CartViewModel = viewModel() + val checkoutInfoViewModel: CheckoutInfoViewModel = viewModel() + DemoAppTheme { Surface( modifier = Modifier.fillMaxSize(), @@ -105,10 +108,15 @@ fun AstronomyShopScreen() { composable(MainDestinations.CHECKOUT_INFO_ROUTE) { InfoScreen( onPlaceOrderClick = {astronomyShopNavController.navigateToCheckoutConfirmation()}, - upPress = {astronomyShopNavController.upPress()}) + upPress = {astronomyShopNavController.upPress()}, + checkoutInfoViewModel = checkoutInfoViewModel + ) } composable(MainDestinations.CHECKOUT_CONFIRMATION_ROUTE){ - CheckoutConfirmationScreen(cartViewModel = cartViewModel) + CheckoutConfirmationScreen( + cartViewModel = cartViewModel, + checkoutInfoViewModel = checkoutInfoViewModel + ) } } } diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt index a58a38a1d..d3995619a 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt @@ -16,8 +16,9 @@ import io.opentelemetry.android.demo.shop.ui.products.ProductCard @Composable fun CheckoutConfirmationScreen( cartViewModel: CartViewModel, -// shippingInfo: ShippingInfo + checkoutInfoViewModel: CheckoutInfoViewModel ) { + val shippingInfo = checkoutInfoViewModel.shippingInfo Column( modifier = Modifier @@ -79,31 +80,31 @@ fun CheckoutConfirmationScreen( style = MaterialTheme.typography.bodyLarge, color = Color.Black ) -// Text( -// text = "Street: ${shippingInfo.streetAddress}", -// style = MaterialTheme.typography.bodyMedium, -// color = Color.Black -// ) -// Text( -// text = "City: ${shippingInfo.city}", -// style = MaterialTheme.typography.bodyMedium, -// color = Color.Black -// ) -// Text( -// text = "State: ${shippingInfo.state}", -// style = MaterialTheme.typography.bodyMedium, -// color = Color.Black -// ) -// Text( -// text = "Zip Code: ${shippingInfo.zipCode}", -// style = MaterialTheme.typography.bodyMedium, -// color = Color.Black -// ) -// Text( -// text = "Country: ${shippingInfo.country}", -// style = MaterialTheme.typography.bodyMedium, -// color = Color.Black -// ) + Text( + text = "Street: ${shippingInfo.streetAddress}", + style = MaterialTheme.typography.bodyMedium, + color = Color.Black + ) + Text( + text = "City: ${shippingInfo.city}", + style = MaterialTheme.typography.bodyMedium, + color = Color.Black + ) + Text( + text = "State: ${shippingInfo.state}", + style = MaterialTheme.typography.bodyMedium, + color = Color.Black + ) + Text( + text = "Zip Code: ${shippingInfo.zipCode}", + style = MaterialTheme.typography.bodyMedium, + color = Color.Black + ) + Text( + text = "Country: ${shippingInfo.country}", + style = MaterialTheme.typography.bodyMedium, + color = Color.Black + ) } } diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt index 305a1c165..0cc9ef7b1 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt @@ -76,7 +76,7 @@ fun InfoFieldsSection( fun InfoScreen( onPlaceOrderClick: () -> Unit, upPress: () -> Unit, - checkoutInfoViewModel: CheckoutInfoViewModel = viewModel() + checkoutInfoViewModel: CheckoutInfoViewModel ) { val shippingInfo by remember { mutableStateOf(checkoutInfoViewModel.shippingInfo) } val paymentInfo by remember { mutableStateOf(checkoutInfoViewModel.paymentInfo) } From c3a1165d5f11fc0fe996f569b85927906bb82933 Mon Sep 17 00:00:00 2001 From: Magda Wojtowicz Date: Thu, 12 Sep 2024 16:37:49 +0200 Subject: [PATCH 04/11] in progress --- .../demo/shop/ui/cart/CheckoutConfirmation.kt | 31 ++++++++++++++----- .../android/demo/shop/ui/cart/CheckoutInfo.kt | 2 -- 2 files changed, 24 insertions(+), 9 deletions(-) diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt index d3995619a..2d5695730 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt @@ -5,11 +5,11 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.* import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import androidx.compose.ui.Alignment import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import io.opentelemetry.android.demo.shop.ui.products.ProductCard @@ -26,7 +26,6 @@ fun CheckoutConfirmationScreen( .padding(16.dp) .verticalScroll(rememberScrollState()) ) { - Text( text = "Your order is complete!", style = MaterialTheme.typography.titleLarge.copy(fontSize = 24.sp), @@ -45,18 +44,27 @@ fun CheckoutConfirmationScreen( .padding(bottom = 32.dp) ) - cartViewModel.cartItems.collectAsState().value.firstOrNull()?.let { cartItem -> + val cartItems = cartViewModel.cartItems.collectAsState().value + cartItems.forEach { cartItem -> ProductCard( product = cartItem.product, onProductClick = {}, isNarrow = true, modifier = Modifier .fillMaxWidth() - .padding(bottom = 16.dp) + .padding(vertical = 8.dp) ) Text( - text = "Total: $${cartViewModel.getTotalPrice()}", + text = "Quantity: ${cartItem.quantity}", + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp) + ) + + Text( + text = "Total: $${String.format("%.2f", cartItem.totalPrice())}", style = MaterialTheme.typography.bodyLarge, modifier = Modifier .fillMaxWidth() @@ -65,6 +73,15 @@ fun CheckoutConfirmationScreen( ) } + Text( + text = "Grand Total: $${cartViewModel.getTotalPrice()}", + style = MaterialTheme.typography.titleMedium, + modifier = Modifier + .fillMaxWidth() + .padding(top = 16.dp, bottom = 16.dp), + textAlign = TextAlign.End + ) + Card( modifier = Modifier .fillMaxWidth() @@ -118,4 +135,4 @@ fun CheckoutConfirmationScreen( Text(text = "Continue Shopping") } } -} \ No newline at end of file +} diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt index 0cc9ef7b1..e0f8a531d 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt @@ -18,7 +18,6 @@ import androidx.compose.ui.unit.dp import io.opentelemetry.android.demo.shop.ui.components.UpPressButton import androidx.compose.ui.Alignment import androidx.compose.ui.text.style.TextAlign -import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun InfoField( @@ -126,7 +125,6 @@ fun InfoScreen( Button( onClick = { - // Proceed to the next screen when clicked onPlaceOrderClick() }, modifier = Modifier.fillMaxWidth(), From 13f9129738c372c30ef12445eba537e1ce56b853 Mon Sep 17 00:00:00 2001 From: Magda Wojtowicz Date: Mon, 16 Sep 2024 16:48:52 +0200 Subject: [PATCH 05/11] changed layout a bit and cleared cart when leaving the confirmation screen using lifecycle owner --- .../demo/shop/ui/cart/CheckoutConfirmation.kt | 104 +++++++++--------- 1 file changed, 53 insertions(+), 51 deletions(-) diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt index 2d5695730..383e2feec 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt @@ -7,17 +7,34 @@ import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver import io.opentelemetry.android.demo.shop.ui.products.ProductCard +import java.util.Locale @Composable fun CheckoutConfirmationScreen( cartViewModel: CartViewModel, checkoutInfoViewModel: CheckoutInfoViewModel ) { + val lifecycleOwner = LocalLifecycleOwner.current + + DisposableEffect(lifecycleOwner) { + val observer = LifecycleEventObserver { _, event -> + if (event == Lifecycle.Event.ON_PAUSE || event == Lifecycle.Event.ON_STOP) { + cartViewModel.clearCart() + } + } + lifecycleOwner.lifecycle.addObserver(observer) + onDispose { + lifecycleOwner.lifecycle.removeObserver(observer) + } + } val shippingInfo = checkoutInfoViewModel.shippingInfo Column( @@ -28,7 +45,7 @@ fun CheckoutConfirmationScreen( ) { Text( text = "Your order is complete!", - style = MaterialTheme.typography.titleLarge.copy(fontSize = 24.sp), + fontSize = 24.sp, textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() @@ -37,7 +54,7 @@ fun CheckoutConfirmationScreen( Text( text = "We've sent you a confirmation email.", - style = MaterialTheme.typography.bodyMedium, + fontSize = 18.sp, textAlign = TextAlign.Center, modifier = Modifier .fillMaxWidth() @@ -46,36 +63,43 @@ fun CheckoutConfirmationScreen( val cartItems = cartViewModel.cartItems.collectAsState().value cartItems.forEach { cartItem -> - ProductCard( - product = cartItem.product, - onProductClick = {}, - isNarrow = true, - modifier = Modifier - .fillMaxWidth() - .padding(vertical = 8.dp) - ) - - Text( - text = "Quantity: ${cartItem.quantity}", - style = MaterialTheme.typography.bodyMedium, + Row( modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 8.dp) - ) + .fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically + ) { + ProductCard( + product = cartItem.product, + onProductClick = {}, + modifier = Modifier + .width(300.dp) + .height(170.dp), + isNarrow = true + ) + Column( + modifier = Modifier + .fillMaxHeight(), + verticalArrangement = Arrangement.Center + ) { + Text( + text = "Quantity: ${cartItem.quantity}", + fontSize = 12.sp, + modifier = Modifier + .padding(horizontal = 8.dp) + ) - Text( - text = "Total: $${String.format("%.2f", cartItem.totalPrice())}", - style = MaterialTheme.typography.bodyLarge, - modifier = Modifier - .fillMaxWidth() - .padding(8.dp), - textAlign = TextAlign.End - ) + Text( + text = "Total: \$${String.format(Locale.US, "%.2f", cartItem.totalPrice())}", + fontSize = 14.sp, + modifier = Modifier + .padding(8.dp), + ) + } + } } Text( - text = "Grand Total: $${cartViewModel.getTotalPrice()}", - style = MaterialTheme.typography.titleMedium, + text = "Total Price: \$${String.format(Locale.US, "%.2f", cartViewModel.getTotalPrice())}", modifier = Modifier .fillMaxWidth() .padding(top = 16.dp, bottom = 16.dp), @@ -86,7 +110,6 @@ fun CheckoutConfirmationScreen( modifier = Modifier .fillMaxWidth() .padding(vertical = 16.dp), - colors = CardDefaults.cardColors(containerColor = Color.LightGray) ) { Column( modifier = Modifier.padding(16.dp), @@ -94,45 +117,24 @@ fun CheckoutConfirmationScreen( ) { Text( text = "Shipping Data", - style = MaterialTheme.typography.bodyLarge, - color = Color.Black + fontWeight = FontWeight.Bold ) Text( text = "Street: ${shippingInfo.streetAddress}", - style = MaterialTheme.typography.bodyMedium, - color = Color.Black ) Text( text = "City: ${shippingInfo.city}", - style = MaterialTheme.typography.bodyMedium, - color = Color.Black ) Text( text = "State: ${shippingInfo.state}", - style = MaterialTheme.typography.bodyMedium, - color = Color.Black ) Text( text = "Zip Code: ${shippingInfo.zipCode}", - style = MaterialTheme.typography.bodyMedium, - color = Color.Black ) Text( text = "Country: ${shippingInfo.country}", - style = MaterialTheme.typography.bodyMedium, - color = Color.Black ) } } - - Button( - onClick = { /* TODO Handle continue shopping action */ }, - modifier = Modifier - .fillMaxWidth() - .height(50.dp) - .align(Alignment.CenterHorizontally) - ) { - Text(text = "Continue Shopping") - } } } From c36a4b485a8bf0dd482aeaf8f71f65c25cabcde9 Mon Sep 17 00:00:00 2001 From: Magda Wojtowicz Date: Tue, 17 Sep 2024 11:58:55 +0200 Subject: [PATCH 06/11] fixed the inability to update payment and shipping info by removing an unnecessary remember --- .../opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt index e0f8a531d..d8365bf39 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutInfo.kt @@ -77,8 +77,8 @@ fun InfoScreen( upPress: () -> Unit, checkoutInfoViewModel: CheckoutInfoViewModel ) { - val shippingInfo by remember { mutableStateOf(checkoutInfoViewModel.shippingInfo) } - val paymentInfo by remember { mutableStateOf(checkoutInfoViewModel.paymentInfo) } + val shippingInfo = checkoutInfoViewModel.shippingInfo + val paymentInfo = checkoutInfoViewModel.paymentInfo val focusManager = LocalFocusManager.current val canProceed = checkoutInfoViewModel.canProceedToCheckout() From c6c1b48d9816227f5e7dc510130c84ddc65a7601 Mon Sep 17 00:00:00 2001 From: Magda Wojtowicz Date: Tue, 17 Sep 2024 12:03:54 +0200 Subject: [PATCH 07/11] added email address to the confirmation screen --- .../android/demo/shop/ui/cart/CheckoutConfirmation.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt index 383e2feec..809470aef 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/cart/CheckoutConfirmation.kt @@ -35,6 +35,7 @@ fun CheckoutConfirmationScreen( lifecycleOwner.lifecycle.removeObserver(observer) } } + val shippingInfo = checkoutInfoViewModel.shippingInfo Column( @@ -53,7 +54,7 @@ fun CheckoutConfirmationScreen( ) Text( - text = "We've sent you a confirmation email.", + text = "We've sent a confirmation email to ${shippingInfo.email}.", fontSize = 18.sp, textAlign = TextAlign.Center, modifier = Modifier From 4bc1515bfd56405b21cc776c4ecbad3cc0d296c6 Mon Sep 17 00:00:00 2001 From: Magda Wojtowicz Date: Tue, 17 Sep 2024 12:40:15 +0200 Subject: [PATCH 08/11] added an order.placed event --- .../demo/shop/ui/AstronomyShopActivity.kt | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/AstronomyShopActivity.kt b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/AstronomyShopActivity.kt index 66be7a043..4b6225412 100644 --- a/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/AstronomyShopActivity.kt +++ b/demo-app/src/main/java/io/opentelemetry/android/demo/shop/ui/AstronomyShopActivity.kt @@ -31,6 +31,7 @@ import io.opentelemetry.android.demo.shop.ui.products.ProductDetails import io.opentelemetry.android.demo.shop.ui.products.ProductList import io.opentelemetry.android.demo.shop.ui.cart.CartViewModel import androidx.lifecycle.viewmodel.compose.viewModel +import io.opentelemetry.android.demo.OtelDemoApplication import io.opentelemetry.android.demo.shop.ui.cart.CheckoutConfirmationScreen import io.opentelemetry.android.demo.shop.ui.cart.CheckoutInfoViewModel import io.opentelemetry.android.demo.shop.ui.cart.InfoScreen @@ -107,7 +108,11 @@ fun AstronomyShopScreen() { } composable(MainDestinations.CHECKOUT_INFO_ROUTE) { InfoScreen( - onPlaceOrderClick = {astronomyShopNavController.navigateToCheckoutConfirmation()}, + onPlaceOrderClick = {instrumentedPlaceOrder( + astronomyShopNavController = astronomyShopNavController, + cartViewModel = cartViewModel, + checkoutInfoViewModel = checkoutInfoViewModel + )}, upPress = {astronomyShopNavController.upPress()}, checkoutInfoViewModel = checkoutInfoViewModel ) @@ -123,3 +128,24 @@ fun AstronomyShopScreen() { } } } + +private fun instrumentedPlaceOrder( + astronomyShopNavController: InstrumentedAstronomyShopNavController, + cartViewModel: CartViewModel, + checkoutInfoViewModel: CheckoutInfoViewModel +){ + generateOrderPlacedEvent(cartViewModel, checkoutInfoViewModel) + astronomyShopNavController.navigateToCheckoutConfirmation() +} + +private fun generateOrderPlacedEvent( + cartViewModel: CartViewModel, + checkoutInfoViewModel: CheckoutInfoViewModel +) { + val eventBuilder = OtelDemoApplication.eventBuilder("otel.demo.app", "order.placed") + eventBuilder + .put("order.total.value", cartViewModel.getTotalPrice()) + .put("buyer.state", checkoutInfoViewModel.shippingInfo.state) + .emit() +} + From c47f36bad91831304ec34a17877a304b2289b5e3 Mon Sep 17 00:00:00 2001 From: Magda Wojtowicz Date: Tue, 17 Sep 2024 15:13:35 +0200 Subject: [PATCH 09/11] updated the Manual Instrumentation bullet --- demo-app/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/demo-app/README.md b/demo-app/README.md index 3c370d377..4838aa6b1 100644 --- a/demo-app/README.md +++ b/demo-app/README.md @@ -53,7 +53,14 @@ The OpenTelemetry Android Demo App currently supports the following features: * Manual Instrumentation - Provides access to the OpenTelemetry APIs for manual instrumentation, allowing developers to create custom spans and events as needed. - - Note: The only manual instrumentation that has been added to the demo app so far is an event after clicking on the OpenTelemetry logo. + - See `OtelDemoApplication.kt` for an example of a tracer and an event builder initialization. + - In the app, custom span is emitted in `MainOtelButton.kt` after clicking on the OpenTelemetry logo button. + - Custom spans are emitted: + - in `MainOtelButton.kt` after clicking on the OpenTelemetry logo button, + - in `Navigation.kt` for screen changes in the app, + - in `AstronomyShopActivity.kt` after placing an order in the shop, + - in `Cart.kt` after emptying a cart in the shop. + - Note: Events aren't visible in the Jaeger UI, only in the collector output. ### Known Gaps As of now, there are a few areas where the instrumentation might not be comprehensive: From 9babbc18122dc1fa38b519a707fcbd18105507ab Mon Sep 17 00:00:00 2001 From: Magda Wojtowicz Date: Tue, 17 Sep 2024 15:14:52 +0200 Subject: [PATCH 10/11] typo --- demo-app/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo-app/README.md b/demo-app/README.md index 4838aa6b1..a1c625662 100644 --- a/demo-app/README.md +++ b/demo-app/README.md @@ -55,7 +55,7 @@ The OpenTelemetry Android Demo App currently supports the following features: - Provides access to the OpenTelemetry APIs for manual instrumentation, allowing developers to create custom spans and events as needed. - See `OtelDemoApplication.kt` for an example of a tracer and an event builder initialization. - In the app, custom span is emitted in `MainOtelButton.kt` after clicking on the OpenTelemetry logo button. - - Custom spans are emitted: + - Custom events are emitted: - in `MainOtelButton.kt` after clicking on the OpenTelemetry logo button, - in `Navigation.kt` for screen changes in the app, - in `AstronomyShopActivity.kt` after placing an order in the shop, From 7999b0a2773e3b1bae289d3053dd27d12c082fe2 Mon Sep 17 00:00:00 2001 From: jason plumb <75337021+breedx-splk@users.noreply.github.com> Date: Fri, 20 Sep 2024 13:28:57 -0700 Subject: [PATCH 11/11] Update demo-app/README.md --- demo-app/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/demo-app/README.md b/demo-app/README.md index a1c625662..4e777e3e0 100644 --- a/demo-app/README.md +++ b/demo-app/README.md @@ -54,7 +54,7 @@ The OpenTelemetry Android Demo App currently supports the following features: * Manual Instrumentation - Provides access to the OpenTelemetry APIs for manual instrumentation, allowing developers to create custom spans and events as needed. - See `OtelDemoApplication.kt` for an example of a tracer and an event builder initialization. - - In the app, custom span is emitted in `MainOtelButton.kt` after clicking on the OpenTelemetry logo button. + - In the app, a custom span is emitted in `MainOtelButton.kt` after clicking on the OpenTelemetry logo button. - Custom events are emitted: - in `MainOtelButton.kt` after clicking on the OpenTelemetry logo button, - in `Navigation.kt` for screen changes in the app,