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 fdd28f8..16c272f 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 @@ -116,6 +116,7 @@ class CardWaitingViewModel( } fun onManualEntryClick() { + stopCardReading() viewModelScope.launch { _events.send(CardWaitingEvent.GoManualEntry) } diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/manual_entry/ManualEntryRoute.kt b/app/src/main/java/com/mob/utsmyanmar/ui/manual_entry/ManualEntryRoute.kt index 7c5af70..b056e0d 100644 --- a/app/src/main/java/com/mob/utsmyanmar/ui/manual_entry/ManualEntryRoute.kt +++ b/app/src/main/java/com/mob/utsmyanmar/ui/manual_entry/ManualEntryRoute.kt @@ -1,8 +1,8 @@ package com.mob.utsmyanmar.ui.manual_entry -import androidx.compose.animation.AnimatedVisibility +import androidx.activity.compose.BackHandler +import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -10,21 +10,18 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.foundation.text.KeyboardActions -import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.ArrowBack -import androidx.compose.material.icons.outlined.CreditCard +import androidx.compose.material.icons.automirrored.rounded.Backspace import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.Scaffold -import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -35,33 +32,37 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.focus.FocusRequester -import androidx.compose.ui.focus.focusRequester -import androidx.compose.ui.platform.LocalFocusManager -import androidx.compose.ui.text.TextRange -import androidx.compose.ui.text.input.ImeAction -import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.text.input.TextFieldValue +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.ui.components.NumericKeypad import com.mob.utsmyanmar.ui.components.appbar.AppBar import com.mob.utsmyanmar.ui.pinpad.PinPadViewModel import com.mob.utsmyanmar.ui.preview.P2Preview -import com.mob.utsmyanmar.ui.preview.P3Preview import com.mob.utsmyanmar.ui.theme.Color import com.mob.utsmyanmar.utils.TransactionUtil import com.mob.utsmyanmar.viewmodel.SharedViewModel import com.mob.utsmyanmar.viewmodel.TransProcessViewModel import com.utsmyanmar.checkxread.model.CardDataX import com.utsmyanmar.checkxread.util.CardTypeX -import com.utsmyanmar.paylibs.model.TradeData import com.utsmyanmar.paylibs.utils.POSUtil -import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType -import sunmi.sunmiui.utils.LogUtil -private const val PAN_RAW_MAX = 16 +private const val PAN_RAW_MAX = 19 +private const val PAN_RAW_MIN = 13 private const val EXPIRY_RAW_MAX = 4 +private enum class ManualEntryStep { + CARD_NUMBER, + EXPIRY +} + +private val digitKeys = listOf( + listOf("1", "2", "3"), + listOf("4", "5", "6"), + listOf("7", "8", "9"), + listOf("", "0", "") +) @Composable fun InputManualCardRoute( @@ -71,50 +72,67 @@ fun InputManualCardRoute( onBack: () -> Unit, onNavigatePinPad: () -> Unit ) { + var step by rememberSaveable { mutableStateOf(ManualEntryStep.CARD_NUMBER) } var pan by rememberSaveable { mutableStateOf("") } var expiry by rememberSaveable { mutableStateOf("") } - var panError by rememberSaveable { mutableStateOf(null) } - var expiryError by rememberSaveable { mutableStateOf(null) } - val focusManager = LocalFocusManager.current + var errorMessage by rememberSaveable { mutableStateOf(null) } LaunchedEffect(Unit) { - pan = ""; expiry = "" - panError = null; expiryError = null + step = ManualEntryStep.CARD_NUMBER + pan = "" + expiry = "" + errorMessage = null } - fun validateAndConfirm() { - panError = null; expiryError = null - val rawPan = pan.filter { it.isDigit() } - val rawExp = expiry.filter { it.isDigit() } + fun goBack() { + when (step) { + ManualEntryStep.EXPIRY -> { + step = ManualEntryStep.CARD_NUMBER + expiry = "" + errorMessage = null + } + ManualEntryStep.CARD_NUMBER -> onBack() + } + } - if (rawPan.length < 13 || rawPan.length > PAN_RAW_MAX) { - panError = "Invalid card number"; return + BackHandler { goBack() } + + fun confirmCardNumber() { + errorMessage = null + if (pan.length < PAN_RAW_MIN || pan.length > PAN_RAW_MAX) { + errorMessage = "Invalid card number" + return } - if (!luhnCheck(rawPan)) { - panError = "Invalid card number"; return + if (!luhnCheck(pan)) { + errorMessage = "Invalid card number" + return } - if (rawExp.length != EXPIRY_RAW_MAX) { - expiryError = "Enter MM/YY"; return + step = ManualEntryStep.EXPIRY + errorMessage = null + } + + fun confirmExpiry() { + errorMessage = null + if (expiry.length != EXPIRY_RAW_MAX) { + errorMessage = "Enter MM/YY" + return } - val month = rawExp.take(2).toIntOrNull() ?: 0 + val month = expiry.take(2).toIntOrNull() ?: 0 if (month < 1 || month > 12) { - expiryError = "Invalid month"; return + errorMessage = "Invalid month" + return } - focusManager.clearFocus() + val expDate = POSUtil.getInstance().getISOFormatExpDate(expiry) + val mpuCardData = CardDataX().apply { + setPan(pan) + setExp(expDate) + setCardHolderName("MANUAL ENTRY") + } - LogUtil.d("Tag","exp date : $rawExp") - val cardNo = rawPan.replace("\\s","") - val expDate = - POSUtil.getInstance().getISOFormatExpDate(rawExp) - - val mpuCardData = CardDataX() - mpuCardData.setPan(cardNo) - mpuCardData.setExp(expDate) - mpuCardData.setCardHolderName("MANUAL ENTRY") - - val tradeData = - TransactionUtil.initMPUTransaction(mpuCardData, CardTypeX.MANUAL) + val tradeData = TransactionUtil.initMPUTransaction(mpuCardData, CardTypeX.MANUAL) + sharedViewModel.setCardDataExist(true) + sharedViewModel.payDetail.value = tradeData.payDetail transProcessViewModel.setTransType(sharedViewModel.transactionsType.value) transProcessViewModel.setTradeData(tradeData) pinPadViewModel.setTradeData(tradeData) @@ -122,311 +140,287 @@ fun InputManualCardRoute( } ManualCardEntryScreen( + step = step, pan = pan, expiry = expiry, - panError = panError, - expiryError = expiryError, - onPanChange = { input -> - val digits = input.filter { it.isDigit() }.take(PAN_RAW_MAX) - pan = digits - panError = null + errorMessage = errorMessage, + onBackClick = ::goBack, + onCancelClick = ::goBack, + onConfirmClick = { + when (step) { + ManualEntryStep.CARD_NUMBER -> confirmCardNumber() + ManualEntryStep.EXPIRY -> confirmExpiry() + } }, - onExpiryChange = { input -> - val digits = input.filter { it.isDigit() }.take(EXPIRY_RAW_MAX) - expiry = digits - expiryError = null + onKeyClick = { key -> + if (key.isBlank()) return@ManualCardEntryScreen + errorMessage = null + when (step) { + ManualEntryStep.CARD_NUMBER -> { + if (pan.length < PAN_RAW_MAX) { + pan += key.filter { it.isDigit() } + } + } + ManualEntryStep.EXPIRY -> { + if (expiry.length < EXPIRY_RAW_MAX) { + expiry += key.filter { it.isDigit() } + } + } + } }, - onBackClick = onBack, - onCancelClick = onBack, - onConfirmClick = ::validateAndConfirm + onDeleteClick = { + errorMessage = null + when (step) { + ManualEntryStep.CARD_NUMBER -> { + if (pan.isNotEmpty()) pan = pan.dropLast(1) + } + ManualEntryStep.EXPIRY -> { + if (expiry.isNotEmpty()) expiry = expiry.dropLast(1) + } + } + } ) } - @Composable private fun ManualCardEntryScreen( + step: ManualEntryStep, pan: String, expiry: String, - panError: String?, - expiryError: String?, - onPanChange: (String) -> Unit, - onExpiryChange: (String) -> Unit, + errorMessage: String?, onBackClick: () -> Unit, onCancelClick: () -> Unit, - onConfirmClick: () -> Unit + onConfirmClick: () -> Unit, + onKeyClick: (String) -> Unit, + onDeleteClick: () -> Unit ) { - val panDisplay = remember(pan) { - formatPan(pan) + val isCardStep = step == ManualEntryStep.CARD_NUMBER + val displayValue = if (isCardStep) formatPanDisplay(pan) else formatExpiryDisplay(expiry) + val prompt = if (isCardStep) "CARD NUMBER" else "EXPIRY DATE" + val supportingText = when { + errorMessage != null -> errorMessage + isCardStep -> "Enter the card number to continue" + else -> "Enter expiry as MM/YY" } - - val expiryDisplay = remember(expiry) { - formatExpiry(expiry) - } - val cardScheme = detectCardScheme(pan) - val confirmEnabled = pan.length >= 13 && expiry.length == 4 - - val expiryFocusRequester = remember { FocusRequester() } - - LaunchedEffect(pan) { - if (pan.length == PAN_RAW_MAX) expiryFocusRequester.requestFocus() + val confirmText = if (isCardStep) "Next" else "Confirm" + val confirmEnabled = if (isCardStep) { + pan.length >= PAN_RAW_MIN + } else { + expiry.length == EXPIRY_RAW_MAX } + val canDelete = if (isCardStep) pan.isNotEmpty() else expiry.isNotEmpty() + val cardScheme = remember(pan) { detectCardScheme(pan) } Scaffold( - topBar = { AppBar(title = "Manual Entry", icon = Icons.AutoMirrored.Rounded.ArrowBack, onIconClick = onBackClick) }, - ) { innerPadding -> + topBar = { + AppBar( + title = "Manual Entry", + icon = Icons.AutoMirrored.Rounded.ArrowBack, + onIconClick = onBackClick + ) + }, + containerColor = Color.IvoryBeige + ) { paddingValues -> Column( modifier = Modifier + .padding(paddingValues) .fillMaxSize() - .padding(innerPadding) - .padding(horizontal = 20.dp, vertical = 16.dp), - verticalArrangement = Arrangement.spacedBy(16.dp) + .padding(horizontal = 20.dp) ) { + Spacer(modifier = Modifier.height(12.dp)) - Surface( - shape = MaterialTheme.shapes.medium, - tonalElevation = 1.dp, - modifier = Modifier.fillMaxWidth() + Text( + text = prompt, + color = Color.Gray, + style = MaterialTheme.typography.labelSmall, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.CenterVertically ) { - Column(modifier = Modifier.padding(16.dp)) { + Text( + text = displayValue.ifBlank { + if (isCardStep) "---- ---- ---- ----" else "--/--" + }, + color = Color.LegacyRed, + style = MaterialTheme.typography.titleLarge.copy( + fontWeight = FontWeight.SemiBold, + letterSpacing = if (isCardStep) 1.sp else 2.sp, + textAlign = TextAlign.Center + ), + textAlign = TextAlign.Center, + maxLines = 2, + modifier = Modifier.weight(1f) + ) - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.padding(bottom = 16.dp) - ) { - Icon( - imageVector = Icons.Outlined.CreditCard, - contentDescription = null, - tint = MaterialTheme.colorScheme.primary, - modifier = Modifier.size(20.dp) - ) - Spacer(Modifier.width(8.dp)) - Text( - "Card details", - style = MaterialTheme.typography.labelLarge, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - if (cardScheme != null) { - Spacer(Modifier.weight(1f)) - SchemeBadge(cardScheme) - } - } + Spacer(modifier = Modifier.width(8.dp)) - OutlinedTextField( - value = panDisplay, - onValueChange = { - onPanChange(it.text) - }, - label = { Text("Card number") }, - placeholder = { Text("0000 0000 0000 0000") }, - isError = panError != null, - supportingText = { - Text(panError ?: " ") // reserve space to avoid layout jump - }, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next - ), - keyboardActions = KeyboardActions( - onNext = { expiryFocusRequester.requestFocus() } - ), - singleLine = true, - modifier = Modifier.fillMaxWidth(), - textStyle = MaterialTheme.typography.bodyLarge.copy( - letterSpacing = 2.sp - ) - ) - - Spacer(Modifier.height(4.dp)) - - OutlinedTextField( - value = expiryDisplay, - onValueChange = { - onExpiryChange(it.text) - }, - label = { Text("Expiry date") }, - placeholder = { Text("MM/YY") }, - isError = expiryError != null, - supportingText = { - Text(expiryError ?: " ") - }, - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Done - ), - keyboardActions = KeyboardActions( - onDone = { onConfirmClick() } - ), - singleLine = true, - modifier = Modifier - .width(160.dp) - .focusRequester(expiryFocusRequester), - textStyle = MaterialTheme.typography.bodyLarge.copy( - letterSpacing = 2.sp - ) - ) - } - } - - AnimatedVisibility(visible = pan.length >= 4) { - Surface( - shape = MaterialTheme.shapes.small, - color = MaterialTheme.colorScheme.surfaceVariant, - modifier = Modifier.fillMaxWidth() + Card( + modifier = Modifier.clickable(enabled = canDelete, onClick = onDeleteClick), + shape = RoundedCornerShape(18.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + elevation = CardDefaults.cardElevation(defaultElevation = 6.dp) ) { - Row( - modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically - ) { - Text( - text = "**** **** **** ${pan.takeLast(4)}", - style = MaterialTheme.typography.bodyMedium, - letterSpacing = 1.5.sp - ) - if (expiry.length == 4) { - Text( - text = "Exp ${expiryDisplay.text}", - style = MaterialTheme.typography.bodySmall, - color = MaterialTheme.colorScheme.onSurfaceVariant - ) - } - } + Icon( + imageVector = Icons.AutoMirrored.Rounded.Backspace, + contentDescription = "Delete", + tint = if (canDelete) Color.LegacyRed else Color.Gray, + modifier = Modifier.padding(horizontal = 14.dp, vertical = 12.dp) + ) } } - Spacer(Modifier.weight(1f)) + Spacer(modifier = Modifier.height(10.dp)) - Box( + Text( + text = supportingText, + color = if (errorMessage == null) Color.Gray else Color.Error, + style = MaterialTheme.typography.labelSmall, + textAlign = TextAlign.Center, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + + if (!isCardStep && pan.length >= 4) { + Spacer(modifier = Modifier.height(12.dp)) + Text( + text = "**** **** **** ${pan.takeLast(4)}" + + (cardScheme?.let { " ยท $it" } ?: ""), + color = Color.Gray, + style = MaterialTheme.typography.labelSmall, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + } + + Spacer(modifier = Modifier.height(28.dp)) + + NumericKeypad( modifier = Modifier .fillMaxWidth() - ){ - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(12.dp) + .height(280.dp), + onKeyClick = onKeyClick, + keys = digitKeys + ) + + Spacer(modifier = Modifier.weight(1f)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Button( + onClick = onCancelClick, + modifier = Modifier + .weight(1f) + .height(56.dp), + shape = RoundedCornerShape(8.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color.White, + contentColor = Color.LegacyRed + ) ) { - Button( - onClick = onBackClick, - modifier = Modifier - .weight(1f) - .height(56.dp), - shape = RoundedCornerShape(8.dp), - colors = ButtonDefaults.buttonColors( - containerColor = Color.White, - contentColor = Color.LegacyRed - ) - ) { - Text("Cancel") - } + Text( + text = if (isCardStep) "Cancel" else "Back", + style = MaterialTheme.typography.titleSmall + ) + } - Button( - onClick = onConfirmClick, - modifier = Modifier - .weight(1f) - .height(56.dp), - - shape = RoundedCornerShape(8.dp), - colors = ButtonDefaults.buttonColors( - containerColor = Color.LegacyRed, - contentColor = Color.White, - disabledContainerColor = Color.LegacyRed.copy(alpha = 0.5f), - disabledContentColor = Color.White - ) - ) { - Text( - text = "Confirm", - style = MaterialTheme.typography.titleSmall - ) - } + Button( + onClick = onConfirmClick, + enabled = confirmEnabled, + modifier = Modifier + .weight(1f) + .height(56.dp), + shape = RoundedCornerShape(8.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color.LegacyRed, + contentColor = Color.White, + disabledContainerColor = Color.LegacyRed.copy(alpha = 0.5f), + disabledContentColor = Color.White + ) + ) { + Text( + text = confirmText, + style = MaterialTheme.typography.titleSmall + ) } } + + Spacer(modifier = Modifier.height(18.dp)) } } } +private fun formatPanDisplay(raw: String): String { + if (raw.isEmpty()) return "" + return raw.chunked(4).joinToString(" ") +} -@Composable -private fun SchemeBadge(scheme: String) { - Surface( - color = MaterialTheme.colorScheme.secondaryContainer, - shape = MaterialTheme.shapes.small - ) { - Text( - text = scheme, - style = MaterialTheme.typography.labelSmall, - color = MaterialTheme.colorScheme.onSecondaryContainer, - modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp) - ) +private fun formatExpiryDisplay(raw: String): String { + return when { + raw.isEmpty() -> "" + raw.length <= 2 -> raw + else -> "${raw.take(2)}/${raw.drop(2)}" } } -private fun formatPan(raw: String): TextFieldValue { - val formatted = raw.chunked(4).joinToString(" ") - - val cursorPos = buildString { - raw.forEachIndexed { index, c -> - append(c) - if ((index + 1) % 4 == 0 && index != raw.lastIndex) { - append(" ") - } - } - }.length - - return TextFieldValue( - text = formatted, - selection = TextRange(cursorPos) - ) -} - -private fun formatExpiry(raw: String): TextFieldValue { - val formatted = when { - raw.length > 2 -> "${raw.take(2)}/${raw.drop(2)}" - else -> raw - } - - val cursorPos = when { - raw.length <= 2 -> raw.length - else -> raw.length + 1 // account for '/' - } - - return TextFieldValue( - text = formatted, - selection = TextRange(cursorPos) - ) -} private fun detectCardScheme(pan: String): String? = when { pan.startsWith("4") -> "Visa" - pan.length >= 2 && (pan.take(2).toInt() in 51..55 || pan.take(4).toIntOrNull() in 2221..2720) -> "Mastercard" + pan.length >= 2 && ( + pan.take(2).toIntOrNull() in 51..55 || + pan.take(4).toIntOrNull() in 2221..2720 + ) -> "Mastercard" pan.length >= 2 && pan.take(2) in listOf("34", "37") -> "Amex" - pan.startsWith("6") -> "UnionPay" + pan.startsWith("62") || pan.startsWith("6") -> "UnionPay / MPU" else -> null } private fun luhnCheck(pan: String): Boolean { - var sum = 0; var alt = false + var sum = 0 + var alt = false for (i in pan.length - 1 downTo 0) { var n = pan[i].digitToIntOrNull() ?: return false - if (alt) { n *= 2; if (n > 9) n -= 9 } - sum += n; alt = !alt + if (alt) { + n *= 2 + if (n > 9) n -= 9 + } + sum += n + alt = !alt } return sum % 10 == 0 } @P2Preview -@P3Preview @Composable -private fun ManualCardEntryScreenPreview() { - MaterialTheme { - ManualCardEntryScreen( - pan = "4111111111111111", - expiry = "1228", - panError = null, - expiryError = null, - onPanChange = {}, - onExpiryChange = {}, - onBackClick = {}, - onCancelClick = {}, - onConfirmClick = {} - ) - } +private fun PreviewManualCardEntryPan() { + ManualCardEntryScreen( + step = ManualEntryStep.CARD_NUMBER, + pan = "6244310039574344", + expiry = "", + errorMessage = null, + onBackClick = {}, + onCancelClick = {}, + onConfirmClick = {}, + onKeyClick = {}, + onDeleteClick = {} + ) } +@P2Preview +@Composable +private fun PreviewManualCardEntryExpiry() { + ManualCardEntryScreen( + step = ManualEntryStep.EXPIRY, + pan = "6244310039574344", + expiry = "1228", + errorMessage = null, + onBackClick = {}, + onCancelClick = {}, + onConfirmClick = {}, + onKeyClick = {}, + onDeleteClick = {} + ) +} 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 f73409e..11c7491 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 @@ -28,6 +28,7 @@ import com.mob.utsmyanmar.ui.functions.FunctionsScreen import com.mob.utsmyanmar.ui.history_functions.HistoryRoute import com.mob.utsmyanmar.ui.history_functions.prepareReprintPayDetail import com.mob.utsmyanmar.ui.input_amount.AmountRoute +import com.mob.utsmyanmar.ui.manual_entry.InputManualCardRoute import com.mob.utsmyanmar.ui.password_input.InputPassword import com.mob.utsmyanmar.ui.password_input.PasswordType import com.mob.utsmyanmar.ui.pinpad.PinPadRoute @@ -554,7 +555,11 @@ fun AppNavGraph( CardWaitingScreen( viewModel = cardWaitingViewModel, amount = formatAmountForDisplay(sharedViewModel.amount.value), - onManualEntry = {}, + onManualEntry = { + navController.navigate(Routes.ManualEntry.route) { + launchSingleTop = true + } + }, onProcessingCard = { navController.navigate(Routes.ProcessingCard.route) { popUpTo(Routes.CardWaiting.route) { @@ -575,6 +580,28 @@ fun AppNavGraph( ) } + composable(Routes.ManualEntry.route) { + val sharedViewModel: SharedViewModel = hiltViewModel(activity) + val transProcessViewModel: TransProcessViewModel = hiltViewModel(activity) + val pinPadViewModel: PinPadViewModel = hiltViewModel(activity) + + InputManualCardRoute( + transProcessViewModel = transProcessViewModel, + sharedViewModel = sharedViewModel, + pinPadViewModel = pinPadViewModel, + onBack = { navController.popBackStack() }, + onNavigatePinPad = { + pinPadViewModel.resetSessionState() + navController.navigate(Routes.PinPad.route) { + popUpTo(Routes.ManualEntry.route) { + inclusive = true + } + launchSingleTop = true + } + } + ) + } + composable(Routes.ProcessingCard.route) { val sharedViewModel: SharedViewModel = hiltViewModel(activity) val cardReaderViewModel: CardReaderViewModel = hiltViewModel(activity)