diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/amount/AmountRoute.kt b/app/src/main/java/com/mob/utsmyanmar/ui/amount/AmountRoute.kt
index 9366700..6290f41 100644
--- a/app/src/main/java/com/mob/utsmyanmar/ui/amount/AmountRoute.kt
+++ b/app/src/main/java/com/mob/utsmyanmar/ui/amount/AmountRoute.kt
@@ -12,20 +12,18 @@ fun AmountRoute(
onBack: () -> Unit,
onNavigateCardWaiting: () -> Unit
) {
- val canGoBack = !action.equals("Sale", ignoreCase = true)
-
AmountScreen(
onBackClick = onBack,
-// onNextClick = { amount ->
-// sharedViewModel.amount.value = amount
-// sharedViewModel.setAmountExist(true)
-// sharedViewModel.setCardDataExist(false)
-// sharedViewModel.setTransMenu(null)
-// sharedViewModel.transactionsType.value = TransactionsType.SALE
-// sharedViewModel.processCode.value =
-// ProcessCode.SALE_PURCHASE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT
-//
-// onNavigateCardWaiting()
-// }
+ onChargeClick = { amount ->
+ sharedViewModel.amount.value = amount
+ sharedViewModel.setAmountExist(true)
+ sharedViewModel.setCardDataExist(false)
+ sharedViewModel.setTransMenu(null)
+ sharedViewModel.transactionsType.value = TransactionsType.SALE
+ sharedViewModel.processCode.value =
+ ProcessCode.SALE_PURCHASE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT
+
+ onNavigateCardWaiting()
+ }
)
}
diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingScreen.kt b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingScreen.kt
index a4773fc..4b0b2a8 100644
--- a/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingScreen.kt
+++ b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingScreen.kt
@@ -1,8 +1,9 @@
package com.mob.utsmyanmar.ui.cardwaiting
import androidx.activity.compose.BackHandler
-import androidx.compose.foundation.Image
+import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
@@ -11,18 +12,22 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.statusBarsPadding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
-import androidx.compose.material3.Button
-import androidx.compose.material3.ButtonDefaults
-import androidx.compose.material3.CenterAlignedTopAppBar
-import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.KeyboardArrowLeft
+import androidx.compose.material.icons.rounded.Wifi
+import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
-import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Surface
import androidx.compose.material3.Text
-import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
@@ -30,23 +35,22 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.draw.rotate
import androidx.compose.ui.res.painterResource
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 com.mob.utsmyanmar.R
-import com.mob.utsmyanmar.ui.theme.Black
-import com.mob.utsmyanmar.ui.theme.Primary
-import com.mob.utsmyanmar.ui.theme.White
+import com.mob.utsmyanmar.ui.preview.P2Preview
+import com.mob.utsmyanmar.ui.theme.Color
-@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CardWaitingScreen(
viewModel: CardWaitingViewModel,
+ amount: String,
onManualEntry: () -> Unit,
- onProcessingCard: () -> Unit,
- onTimeout: () -> Unit,
onBack: () -> Unit,
onMain: () -> Unit
) {
@@ -56,10 +60,10 @@ fun CardWaitingScreen(
viewModel.events.collect { event ->
when (event) {
CardWaitingEvent.GoManualEntry -> onManualEntry()
- CardWaitingEvent.GoProcessingCard -> onProcessingCard()
- CardWaitingEvent.GoTimeout -> onTimeout()
CardWaitingEvent.GoMain -> onMain()
CardWaitingEvent.GoBack -> onBack()
+ CardWaitingEvent.GoProcessingCard,
+ CardWaitingEvent.GoTimeout -> Unit
}
}
}
@@ -78,160 +82,360 @@ fun CardWaitingScreen(
viewModel.onBackPressed()
}
- Scaffold(
- topBar = {
- CenterAlignedTopAppBar(
- title = {
- Text(
- text = "CARD CAPTURE",
- color = White,
- fontSize = 16.sp,
- fontWeight = FontWeight.SemiBold
- )
- },
- navigationIcon = {
- if (uiState.canGoBack) {
- IconButton(onClick = onBack) {
- Icon(
- painter = painterResource(R.drawable.ic_left_arrow),
- contentDescription = "Back",
- tint = White
- )
- }
- }
- },
- colors = TopAppBarDefaults.topAppBarColors(
- containerColor = Primary
- )
- )
- },
- containerColor = White
- ) { paddingValues ->
- Column(
+ CardWaitingScreenContent(
+ amount = amount,
+ uiState = uiState,
+ onBackClick = viewModel::onBackPressed,
+ onManualEntryClick = viewModel::onManualEntryClick
+ )
+}
+
+@Composable
+private fun CardWaitingScreenContent(
+ amount: String,
+ uiState: CardWaitingUiState,
+ onBackClick: () -> Unit,
+ onManualEntryClick: () -> Unit
+) {
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .background(Color.IvoryBeige)
+ .statusBarsPadding()
+ .navigationBarsPadding()
+ .padding(horizontal = 20.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Box(
modifier = Modifier
- .fillMaxSize()
- .padding(paddingValues)
- .background(White)
+ .fillMaxWidth()
+ .height(54.dp),
+ contentAlignment = Alignment.Center
) {
-
- Box(
- modifier = Modifier
- .fillMaxSize()
- .background(
- color = Primary,
- shape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp)
- )
- .padding(18.dp)
- ) {
- Column(
- modifier = Modifier.fillMaxSize(),
- horizontalAlignment = Alignment.CenterHorizontally
+ if (uiState.canGoBack) {
+ IconButton(
+ onClick = onBackClick,
+ modifier = Modifier.align(Alignment.CenterStart)
) {
- Box(
+ Icon(
+ imageVector = Icons.Rounded.KeyboardArrowLeft,
+ contentDescription = "Back",
+ tint = Color.LegacyRed
+ )
+ }
+ }
+ }
+
+ Spacer(modifier = Modifier.height(8.dp))
+
+ Text(
+ text = "AMOUNT TO PAY",
+ color = Color.Black,
+ fontSize = 10.sp,
+ fontWeight = FontWeight.Medium
+ )
+
+ Spacer(modifier = Modifier.height(6.dp))
+
+ Row(verticalAlignment = Alignment.Bottom) {
+ Text(
+ text = amount,
+ color = Color.LegacyRed,
+ fontSize = 30.sp,
+ fontWeight = FontWeight.Bold
+ )
+
+ Spacer(modifier = Modifier.width(6.dp))
+
+ Text(
+ text = "MMK",
+ color = Color.LegacyRed,
+ fontSize = 10.sp,
+ fontWeight = FontWeight.Bold,
+ modifier = Modifier.padding(bottom = 6.dp)
+ )
+ }
+
+ Spacer(modifier = Modifier.height(32.dp))
+
+ ContactlessCircle(isLoading = uiState.isLoading)
+
+ Spacer(modifier = Modifier.height(24.dp))
+
+ Text(
+ text = when {
+ uiState.isCardCaptured -> "Card Detected"
+ uiState.isFallback -> "Swipe Your Card"
+ else -> "Tap Your Card"
+ },
+ color = Color.LegacyRed,
+ fontSize = 13.sp,
+ fontWeight = FontWeight.Bold
+ )
+
+ Text(
+ text = if (uiState.isCardCaptured) {
+ "Reader captured card data"
+ } else {
+ "Hold your card near the reader"
+ },
+ color = Color.Black,
+ fontSize = 9.sp
+ )
+
+ Spacer(modifier = Modifier.height(14.dp))
+
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ HorizontalDivider(
+ modifier = Modifier.weight(1f),
+ color = Color.Gray.copy(alpha = 0.4f)
+ )
+
+ Text(
+ text = "OR",
+ modifier = Modifier.padding(horizontal = 12.dp),
+ color = Color.Gray,
+ fontSize = 10.sp
+ )
+
+ HorizontalDivider(
+ modifier = Modifier.weight(1f),
+ color = Color.Gray.copy(alpha = 0.4f)
+ )
+ }
+
+ Spacer(modifier = Modifier.height(12.dp))
+
+ InsertCardRow()
+
+ Spacer(modifier = Modifier.height(18.dp))
+
+ Text(
+ text = uiState.alertMessage,
+ color = Color.Black,
+ fontSize = 11.sp,
+ textAlign = TextAlign.Center,
+ lineHeight = 16.sp
+ )
+
+ Spacer(modifier = Modifier.height(18.dp))
+
+ StatusPanel(uiState = uiState)
+
+ Spacer(modifier = Modifier.weight(1f))
+
+ ManualEntryAction(onManualEntryClick = onManualEntryClick)
+
+ Spacer(modifier = Modifier.height(18.dp))
+
+ CardLogoRow()
+
+ Spacer(modifier = Modifier.height(34.dp))
+
+ PoweredByMob()
+
+ Spacer(modifier = Modifier.height(12.dp))
+ }
+}
+
+@Composable
+private fun ContactlessCircle(isLoading: Boolean) {
+ Box(
+ modifier = Modifier.size(190.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ CircleBorder(180, 0.1f)
+ CircleBorder(150, 0.20f)
+
+ Surface(
+ modifier = Modifier.size(108.dp),
+ shape = CircleShape,
+ color = Color.White,
+ shadowElevation = 8.dp
+ ) {
+ Box(contentAlignment = Alignment.Center) {
+ if (isLoading) {
+ CircularProgressIndicator(
+ color = Color.LegacyRed,
+ strokeWidth = 3.dp,
+ modifier = Modifier.size(40.dp)
+ )
+ } else {
+ Icon(
+ imageVector = Icons.Rounded.Wifi,
+ contentDescription = null,
+ tint = Color.LegacyRed,
modifier = Modifier
- .fillMaxWidth()
- .background(
- color = White,
- shape = RoundedCornerShape(24.dp)
- )
- .padding(horizontal = 22.dp, vertical = 28.dp)
- ) {
- Column(
- modifier = Modifier.fillMaxWidth(),
- horizontalAlignment = Alignment.CenterHorizontally
- ) {
- Box(
- modifier = Modifier
- .size(92.dp)
- .background(
- color = Primary.copy(alpha = 0.08f),
- shape = RoundedCornerShape(28.dp)
- ),
- contentAlignment = Alignment.Center
- ) {
- Image(
- painter = painterResource(R.drawable.buy_ecommerce_finance_payment_pos_shop_svgrepo_com),
- contentDescription = "Card reader"
- )
- }
-
- Spacer(modifier = Modifier.height(20.dp))
-
- Text(
- text = if (uiState.isFallback) {
- "Swipe Card"
- } else {
- "Present Card"
- },
- color = Black,
- fontSize = 24.sp,
- fontWeight = FontWeight.Bold
- )
-
- Spacer(modifier = Modifier.height(10.dp))
-
- Text(
- text = uiState.alertMessage,
- color = Black,
- fontSize = 18.sp,
- lineHeight = 26.sp,
- textAlign = TextAlign.Center
- )
- }
- }
-
- Spacer(modifier = Modifier.height(16.dp))
-
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .background(
- color = White.copy(alpha = 0.12f),
- shape = RoundedCornerShape(22.dp)
- )
- .padding(horizontal = 18.dp, vertical = 16.dp),
- horizontalArrangement = Arrangement.SpaceBetween,
- verticalAlignment = Alignment.CenterVertically
- ) {
- Text(
- text = if (uiState.isLoading) {
- "Reader status"
- } else {
- "Ready for card"
- },
- color = White,
- fontSize = 16.sp,
- fontWeight = FontWeight.SemiBold
- )
-
- Text(
- text = if (uiState.isLoading) "Initializing" else "Waiting",
- color = White,
- fontSize = 15.sp
- )
- }
-
- Spacer(modifier = Modifier.weight(1f))
-
- Button(
- onClick = viewModel::onManualEntryClick,
- modifier = Modifier
- .fillMaxWidth()
- .height(64.dp),
- shape = RoundedCornerShape(18.dp),
- colors = ButtonDefaults.buttonColors(
- containerColor = White,
- contentColor = Primary
- )
- ) {
- Text(
- text = "Manual Entry",
- fontSize = 20.sp,
- fontWeight = FontWeight.Bold
- )
- }
+ .size(72.dp)
+ .rotate(90f)
+ )
}
}
}
}
}
+
+@Composable
+private fun CircleBorder(
+ size: Int,
+ alpha: Float
+) {
+ Surface(
+ modifier = Modifier
+ .size(size.dp)
+ .alpha(alpha),
+ shape = CircleShape,
+ border = BorderStroke(1.dp, Color.LegacyRed)
+ ) {}
+}
+
+@Composable
+private fun InsertCardRow() {
+ Row(
+ modifier = Modifier.height(44.dp),
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Icon(
+ painter = painterResource(id = R.drawable.ic_insert_card),
+ contentDescription = null,
+ tint = Color.LegacyRed,
+ modifier = Modifier.size(28.dp)
+ )
+
+ Spacer(modifier = Modifier.width(10.dp))
+
+ Column {
+ Text(
+ text = "Insert Your Card",
+ color = Color.LegacyRed,
+ fontSize = 11.sp,
+ fontWeight = FontWeight.Bold
+ )
+ Text(
+ text = "Chip facing up",
+ color = Color.Black,
+ fontSize = 9.sp
+ )
+ }
+ }
+}
+
+@Composable
+private fun StatusPanel(uiState: CardWaitingUiState) {
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ color = Color.White,
+ shape = RoundedCornerShape(12.dp)
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 14.dp, vertical = 12.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text(
+ text = when {
+ uiState.isCardCaptured -> "Reader status"
+ uiState.isLoading -> "Reader status"
+ else -> "Ready for card"
+ },
+ color = Color.LegacyRed,
+ fontSize = 12.sp,
+ fontWeight = FontWeight.Bold
+ )
+
+ Text(
+ text = when {
+ uiState.isCardCaptured -> "Captured"
+ uiState.isLoading -> "Initializing"
+ else -> "Waiting"
+ },
+ color = Color.Gray,
+ fontSize = 11.sp
+ )
+ }
+ }
+}
+
+@Composable
+private fun ManualEntryAction(onManualEntryClick: () -> Unit) {
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ color = Color.White,
+ shape = RoundedCornerShape(12.dp)
+ ) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .background(Color.White)
+ .clickable(onClick = onManualEntryClick)
+ .padding(vertical = 16.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Text(
+ text = "Manual Entry",
+ color = Color.LegacyRed,
+ fontSize = 14.sp,
+ fontWeight = FontWeight.Bold
+ )
+ }
+ }
+}
+
+@Composable
+private fun CardLogoRow() {
+ Surface(
+ modifier = Modifier.fillMaxWidth(),
+ color = Color.White,
+ shape = RoundedCornerShape(3.dp)
+ ) {
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(42.dp)
+ .padding(horizontal = 14.dp),
+ horizontalArrangement = Arrangement.SpaceBetween,
+ verticalAlignment = Alignment.CenterVertically
+ ) {
+ Text("VISA", fontSize = 22.sp, fontWeight = FontWeight.Bold)
+ Text("MC", fontSize = 18.sp, fontWeight = FontWeight.Bold)
+ Text("MPU", fontSize = 20.sp, fontWeight = FontWeight.Bold)
+ Text("UnionPay", fontSize = 12.sp, fontWeight = FontWeight.Bold)
+ }
+ }
+}
+
+@Composable
+private fun PoweredByMob() {
+ Row(verticalAlignment = Alignment.CenterVertically) {
+ Text(
+ text = "Powered by",
+ color = Color.Gray,
+ fontSize = 8.sp
+ )
+
+ Spacer(modifier = Modifier.width(4.dp))
+
+ Text(
+ text = "MOB",
+ color = Color.Gray,
+ fontSize = 9.sp,
+ fontWeight = FontWeight.Bold
+ )
+ }
+}
+
+@P2Preview
+@Composable
+fun PreviewCardWaitingScreen() {
+ CardWaitingScreenContent(
+ amount = "50,000",
+ uiState = CardWaitingUiState(),
+ onBackClick = {},
+ onManualEntryClick = {}
+ )
+}
diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingUiState.kt b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingUiState.kt
index eada451..2dddc5c 100644
--- a/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingUiState.kt
+++ b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingUiState.kt
@@ -4,5 +4,6 @@ data class CardWaitingUiState(
val alertMessage: String = "Please insert, tap, or swipe card",
val isLoading: Boolean = false,
val isFallback: Boolean = false,
- val canGoBack: Boolean = true
+ val canGoBack: Boolean = true,
+ val isCardCaptured: Boolean = false
)
diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingViewModel.kt b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingViewModel.kt
index 9ba9709..c8c95c8 100644
--- a/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingViewModel.kt
+++ b/app/src/main/java/com/mob/utsmyanmar/ui/cardwaiting/CardWaitingViewModel.kt
@@ -78,7 +78,17 @@ class CardWaitingViewModel(
_uiState.update {
it.copy(
alertMessage = "Fallback!\nPlease stripe!",
- isFallback = true
+ isFallback = true,
+ isCardCaptured = false
+ )
+ }
+ } else {
+ _uiState.update {
+ it.copy(
+ alertMessage = "Please insert, tap, or swipe card",
+ isFallback = false,
+ isLoading = false,
+ isCardCaptured = false
)
}
}
@@ -122,7 +132,8 @@ class CardWaitingViewModel(
_uiState.update {
it.copy(
alertMessage = "Initializing card reader...",
- isLoading = true
+ isLoading = true,
+ isCardCaptured = false
)
}
@@ -136,7 +147,8 @@ class CardWaitingViewModel(
} else {
"Please insert, tap, or swipe card"
},
- isLoading = false
+ isLoading = false,
+ isCardCaptured = false
)
}
setupCardReadProcess(isFallback)
@@ -209,8 +221,14 @@ class CardWaitingViewModel(
}
}
- viewModelScope.launch {
- _events.send(CardWaitingEvent.GoProcessingCard)
+ stopCardReading()
+
+ _uiState.update {
+ it.copy(
+ alertMessage = "Card detected.\nOnline process disabled.",
+ isLoading = false,
+ isCardCaptured = true
+ )
}
}
diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt
index 9d3d780..d2a747c 100644
--- a/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt
+++ b/app/src/main/java/com/mob/utsmyanmar/ui/navigation/AppNavGraph.kt
@@ -87,16 +87,8 @@ fun AppNavGraph(
CardWaitingScreen(
viewModel = cardWaitingViewModel,
+ amount = formatAmountForDisplay(sharedViewModel.amount.value),
onManualEntry = {},
- onProcessingCard = {
- navController.navigate(Routes.ProcessingCard.route) {
- popUpTo(Routes.CardWaiting.route) {
- inclusive = true
- }
- launchSingleTop = true
- }
- },
- onTimeout = { navController.popBackStack() },
onBack = { navController.popBackStack() },
onMain = {
navController.navigate(Routes.Dashboard.route) {
@@ -207,3 +199,9 @@ fun AppNavGraph(
}
}
}
+
+private fun formatAmountForDisplay(amount: String?): String {
+ val normalizedAmount = amount.orEmpty()
+ val value = normalizedAmount.toLongOrNull() ?: return normalizedAmount.ifBlank { "0" }
+ return "%,d".format(value)
+}
diff --git a/app/src/main/res/drawable/ic_insert_card.xml b/app/src/main/res/drawable/ic_insert_card.xml
new file mode 100644
index 0000000..7f40a58
--- /dev/null
+++ b/app/src/main/res/drawable/ic_insert_card.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_wifi.xml b/app/src/main/res/drawable/ic_wifi.xml
new file mode 100644
index 0000000..ed9a35f
--- /dev/null
+++ b/app/src/main/res/drawable/ic_wifi.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+