void screen and pinpad fix

This commit is contained in:
moon 2026-05-21 23:31:05 +06:30
parent 675e398aaf
commit dbc5424aab
13 changed files with 628 additions and 102 deletions

2
.gitignore vendored
View File

@ -15,3 +15,5 @@
local.properties
/.idea
/.idea
/sunmiui-lib
/paysdk-lib

View File

@ -47,6 +47,7 @@ dependencies {
implementation(libs.androidx.compose.foundation)
implementation(libs.androidx.compose.material3)
implementation(libs.androidx.compose.runtime)
implementation(libs.androidx.compose.runtime.livedata)
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
implementation(libs.androidx.compose.ui.tooling.preview)

View File

@ -13,17 +13,77 @@ fun AmountRoute(
onNavigateCardWaiting: () -> Unit
) {
AmountScreen(
title = action.ifBlank { "Amount" },
onBackClick = onBack,
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
val config = action.toTransactionConfig()
sharedViewModel.transactionName.value = config.transactionName
sharedViewModel.transactionsType.value = config.transactionType
sharedViewModel.processCode.value = config.processCode
onNavigateCardWaiting()
}
)
}
private data class TransactionConfig(
val transactionName: String,
val transactionType: TransactionsType,
val processCode: String
)
private fun String.toTransactionConfig(): TransactionConfig {
return when (trim().lowercase()) {
"void" -> TransactionConfig(
transactionName = "SALE",
transactionType = TransactionsType.VOID,
processCode = ProcessCode.SALE_VOID + ProcessCode.SMART + ProcessCode.TO_ACCOUNT
)
"refund" -> TransactionConfig(
transactionName = "REFUND",
transactionType = TransactionsType.REFUND,
processCode = ProcessCode.REFUND + ProcessCode.SMART + ProcessCode.TO_ACCOUNT
)
"pre-auth" -> TransactionConfig(
transactionName = "PRE_AUTH",
transactionType = TransactionsType.PRE_AUTH_SALE,
processCode = ProcessCode.PRE_AUTH_SALE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT
)
"pre-auth void" -> TransactionConfig(
transactionName = "PRE_AUTH",
transactionType = TransactionsType.PRE_AUTH_VOID,
processCode = ProcessCode.PRE_AUTH_VOID + ProcessCode.SMART + ProcessCode.TO_ACCOUNT
)
"pre-auth complete" -> TransactionConfig(
transactionName = "PRE_AUTH_COMPLETE",
transactionType = TransactionsType.PRE_AUTH_COMPLETE,
processCode = ProcessCode.PRE_AUTH_COMPLETE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT
)
"pre-auth complete void" -> TransactionConfig(
transactionName = "PRE_AUTH_COMPLETE",
transactionType = TransactionsType.PRE_AUTH_COMPLETE_VOID,
processCode = ProcessCode.PRE_AUTH_COMPLETE_VOID + ProcessCode.SMART + ProcessCode.TO_ACCOUNT
)
"cash out" -> TransactionConfig(
transactionName = "CASH_OUT",
transactionType = TransactionsType.CASH_OUT,
processCode = ProcessCode.CASH_ADVANCE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT
)
else -> TransactionConfig(
transactionName = "SALE",
transactionType = TransactionsType.SALE,
processCode = ProcessCode.SALE_PURCHASE + ProcessCode.SMART + ProcessCode.TO_ACCOUNT
)
}
}

View File

@ -22,6 +22,7 @@ import com.mob.utsmyanmar.ui.theme.Color
@Composable
fun AmountScreen(
title: String = "Amount",
onBackClick: () -> Unit = {},
onChargeClick: (String) -> Unit = {}
) {
@ -59,7 +60,7 @@ fun AmountScreen(
}
Text(
text = "Amount",
text = title,
color = Color.LegacyRed,
fontSize = 14.sp,
fontWeight = FontWeight.Bold
@ -69,7 +70,7 @@ fun AmountScreen(
Spacer(modifier = Modifier.height(36.dp))
Text(
text = "Enter Amount",
text = "Enter $title",
color = Color.Gray,
fontSize = 11.sp,
modifier = Modifier.align(Alignment.CenterHorizontally)
@ -100,7 +101,7 @@ fun AmountScreen(
Spacer(modifier = Modifier.height(12.dp))
Text(
text = "Enter the amount to charge",
text = "Enter the amount to continue",
color = Color.Gray,
fontSize = 11.sp,
modifier = Modifier.align(Alignment.CenterHorizontally)

View File

@ -224,7 +224,7 @@ private fun CardWaitingScreenContent(
Spacer(modifier = Modifier.height(18.dp))
StatusPanel(uiState = uiState)
// StatusPanel(uiState = uiState)
Spacer(modifier = Modifier.weight(1f))

View File

@ -84,6 +84,7 @@ import kotlinx.coroutines.launch
fun DashboardScreen2(
onNavigateAmount: (String) -> Unit = {},
onNavigateSignOn: () -> Unit = {},
onNavigateSeeMore: () -> Unit = {},
deviceInfoViewModel: DeviceInfoViewModel = viewModel()
) {
val deviceInfo by deviceInfoViewModel.uiState.collectAsState()
@ -91,8 +92,7 @@ fun DashboardScreen2(
LaunchedEffect(Unit) {
deviceInfoViewModel.loadDeviceInfo();
}
val drawerState = rememberDrawerState(initialValue = androidx.compose.material3.DrawerValue.Open) //for preview
// val drawerState = rememberDrawerState(initialValue = androidx.compose.material3.DrawerValue.Closed) //for normal usage
val drawerState = rememberDrawerState(initialValue = androidx.compose.material3.DrawerValue.Closed)
val scope = rememberCoroutineScope()
val mainHandler = remember { Handler(Looper.getMainLooper()) }
var showHostActionDialog by remember { mutableStateOf(false) }
@ -300,7 +300,8 @@ fun DashboardScreen2(
Spacer(modifier = Modifier.height(16.dp))
MenuGrid(
onNavigateAmount = onNavigateAmount,
onNavigateSignOn = onNavigateSignOn
onNavigateSignOn = onNavigateSignOn,
onNavigateSeeMore = onNavigateSeeMore
)
}
}
@ -494,7 +495,8 @@ private fun IconCircle(
@Composable
private fun MenuGrid(
onNavigateAmount: (String) -> Unit,
onNavigateSignOn: () -> Unit
onNavigateSignOn: () -> Unit,
onNavigateSeeMore: () -> Unit
) {
Column(
verticalArrangement = Arrangement.spacedBy(10.dp),
@ -519,7 +521,12 @@ private fun MenuGrid(
onClick = onNavigateSignOn
)
MenuCard("Settlement", Icons.Default.Wallet, Modifier.weight(1f))
MenuCard("See More", Icons.Default.GridView, Modifier.weight(1f))
MenuCard(
"See More",
Icons.Default.GridView,
Modifier.weight(1f),
onClick = onNavigateSeeMore
)
}
}
}

View File

@ -0,0 +1,141 @@
package com.mob.utsmyanmar.ui.dashboard
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
import androidx.compose.foundation.layout.PaddingValues
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.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.AccountBalanceWallet
import androidx.compose.material.icons.filled.CreditCard
import androidx.compose.material.icons.filled.Lock
import androidx.compose.material.icons.filled.LockOpen
import androidx.compose.material.icons.filled.Replay
import androidx.compose.material.icons.filled.SwapHoriz
import androidx.compose.material.icons.filled.Undo
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
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.appbar.AppBar
import com.mob.utsmyanmar.ui.preview.P2Preview
import com.mob.utsmyanmar.ui.theme.Color
@Composable
fun SeeMoreScreen(
onBack: () -> Unit = {},
onNavigateAmount: (String) -> Unit = {}
) {
val items = listOf(
SeeMoreItem("Void", Icons.Default.Undo),
SeeMoreItem("Refund", Icons.Default.Replay),
SeeMoreItem("Pre-Auth", Icons.Default.Lock),
SeeMoreItem("Pre-Auth Void", Icons.Default.LockOpen),
SeeMoreItem("Pre-Auth Complete", Icons.Default.CreditCard),
SeeMoreItem("Pre-Auth Complete Void", Icons.Default.SwapHoriz),
SeeMoreItem("Cash Out", Icons.Default.AccountBalanceWallet)
)
Scaffold(
containerColor = Color.IvoryBeige,
topBar = {
AppBar(
title = "See More",
icon = Icons.AutoMirrored.Filled.ArrowBack,
onIconClick = onBack
)
}
) { paddingValues ->
LazyVerticalGrid(
columns = GridCells.Fixed(2),
modifier = Modifier
.fillMaxSize()
.padding(paddingValues),
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
items(items) { item ->
MoreMenuCard(
title = item.title,
icon = item.icon,
onClick = { onNavigateAmount(item.title) }
)
}
}
}
}
@Composable
private fun MoreMenuCard(
title: String,
icon: ImageVector,
onClick: () -> Unit
) {
Card(
modifier = Modifier
.fillMaxWidth()
.height(132.dp)
.clickable(onClick = onClick),
shape = RoundedCornerShape(18.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(4.dp)
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(14.dp),
verticalArrangement = Arrangement.SpaceBetween
) {
Box(
modifier = Modifier
.background(Color.CrimsonRed, RoundedCornerShape(14.dp))
.padding(12.dp)
) {
Icon(
imageVector = icon,
contentDescription = title,
tint = Color.White
)
}
Text(
text = title,
color = Color.Black,
fontSize = 14.sp,
fontWeight = FontWeight.SemiBold,
textAlign = TextAlign.Start
)
}
}
}
private data class SeeMoreItem(
val title: String,
val icon: ImageVector
)
@P2Preview
@Composable
private fun PreviewSeeMoreScreen() {
SeeMoreScreen()
}

View File

@ -15,6 +15,7 @@ import com.mob.utsmyanmar.ui.amount.AmountRoute
import com.mob.utsmyanmar.ui.cardwaiting.CardWaitingScreen
import com.mob.utsmyanmar.ui.cardwaiting.CardWaitingViewModel
import com.mob.utsmyanmar.ui.dashboard.DashboardScreen2
import com.mob.utsmyanmar.ui.dashboard.SeeMoreScreen
import com.mob.utsmyanmar.ui.pinpad.PinPadRoute
import com.mob.utsmyanmar.ui.processing_card.ProcessingCardRoute
import com.mob.utsmyanmar.ui.processing_card.ProcessingCardViewModel
@ -23,6 +24,7 @@ import com.mob.utsmyanmar.ui.sign_on.SignOnResultScreen
import com.mob.utsmyanmar.ui.sign_on.SignOnRoute
import com.mob.utsmyanmar.ui.sending_to_host.SendingToHostRoute
import com.mob.utsmyanmar.ui.transaction_result.TransactionResultRoute
import com.mob.utsmyanmar.ui.void_trace.VoidTraceScreen
import com.mob.utsmyanmar.viewmodel.CardReaderViewModel
import com.mob.utsmyanmar.viewmodel.EmvTransactionProcessViewModel
import com.mob.utsmyanmar.ui.pinpad.PinPadViewModel
@ -54,10 +56,41 @@ fun AppNavGraph(
navController.navigate(Routes.SignOn.route) {
launchSingleTop = true
}
},
onNavigateSeeMore = {
navController.navigate(Routes.SeeMore.route) {
launchSingleTop = true
}
}
)
}
composable(Routes.SeeMore.route) {
SeeMoreScreen(
onBack = { navController.popBackStack() },
onNavigateAmount = { action ->
if (action == "Void") {
navController.navigate(Routes.VoidTrace.route) {
launchSingleTop = true
}
} else {
navController.navigate(Routes.Amount.createRoute(action)) {
launchSingleTop = true
}
}
}
)
}
composable(Routes.VoidTrace.route) {
val sharedViewModel: SharedViewModel = hiltViewModel(activity)
VoidTraceScreen(
sharedViewModel = sharedViewModel,
onBack = { navController.popBackStack() }
)
}
composable(Routes.SignOn.route) {
SignOnRoute(
onBack = { navController.popBackStack() },
@ -187,6 +220,7 @@ fun AppNavGraph(
ProcessingCardRoute(
viewModel = processingCardViewModel,
onNavigatePinPad = {
pinPadViewModel.resetSessionState()
navController.navigate(Routes.PinPad.route) {
popUpTo(Routes.ProcessingCard.route) {
inclusive = true

View File

@ -5,8 +5,10 @@ import android.net.Uri
sealed class Routes(val route: String) {
data object Dashboard : Routes("dashboard")
data object Amount : Routes("amount/{action}") {
fun createRoute(action: String): String = "amount/$action"
fun createRoute(action: String): String = "amount/${Uri.encode(action)}"
}
data object SeeMore : Routes("see_more")
data object VoidTrace : Routes("void_trace")
data object SignOn : Routes("sign_on")
data object SignOnResult : Routes("sign_on_result/{isSuccess}/{message}") {
fun createRoute(isSuccess: Boolean, message: String): String {

View File

@ -116,6 +116,13 @@ class PinPadViewModel @Inject constructor(
_transType.value = type
}
fun resetSessionState() {
_pinText.value = ""
_alertMsg.value = null
_errorCode.value = null
_pinStatus.value = null
}
/*
* Pin Pad
*/
@ -123,7 +130,8 @@ class PinPadViewModel @Inject constructor(
fun startPinPadProcess(
customPinPadKeyboard: CustomPinPadKeyboard
) {
resetSessionState()
// dukptIndex = SystemParamsOperation.getInstance().tmkIndex.toInt()
if(!sunmiPayManager.isReady()){
_alertMsg.value =

View File

@ -0,0 +1,354 @@
package com.mob.utsmyanmar.ui.void_trace
import android.util.Log
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
import androidx.compose.foundation.layout.Row
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.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.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.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.mob.utsmyanmar.ui.components.appbar.AppBar
import com.mob.utsmyanmar.ui.theme.Color
import com.mob.utsmyanmar.viewmodel.SharedViewModel
import com.utsmyanmar.paylibs.model.PayDetail
import com.utsmyanmar.paylibs.utils.POSUtil
import com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType
@Composable
fun VoidTraceScreen(
sharedViewModel: SharedViewModel,
onBack: () -> Unit = {}
) {
val tag = "VoidTraceScreen"
var traceNumber by remember { mutableStateOf("") }
var searchedTrace by remember { mutableStateOf("") }
val recentTransactions by sharedViewModel.getLastThreeTransactions().observeAsState(emptyList())
val transactionSource: LiveData<PayDetail?> = remember(searchedTrace) {
if (searchedTrace.isBlank()) {
MutableLiveData(null)
} else {
MutableLiveData(null)
}
}
val transaction by transactionSource.observeAsState()
val hasSearched = searchedTrace.isNotBlank()
val headerMessage = if (hasSearched && transaction == null) {
"Warning: no previous sale transaction found for trace $searchedTrace."
} else {
"Input trace number to find previous sale transaction."
}
val headerColor = if (hasSearched && transaction == null) {
Color.LegacyRed
} else {
Color.Black
}
LaunchedEffect(recentTransactions) {
Log.d(
tag,
"Recent DB traces: ${
recentTransactions.joinToString { detail ->
"${detail.voucherNo}:${detail.transType}:${detail.tradeAnswerCode}"
}
}"
)
}
LaunchedEffect(searchedTrace, transaction) {
if (searchedTrace.isNotBlank()) {
Log.d(
tag,
"Search trace=$searchedTrace result=${transaction?.voucherNo ?: "NOT_FOUND"}"
)
}
}
Scaffold(
containerColor = Color.IvoryBeige,
topBar = {
AppBar(
title = "Void",
icon = Icons.AutoMirrored.Filled.ArrowBack,
onIconClick = onBack
)
}
) { paddingValues ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = 16.dp)
) {
Text(
text = headerMessage,
color = headerColor,
fontSize = 14.sp
)
Spacer(modifier = Modifier.height(28.dp))
Text(
text = "Trace Number",
color = Color.Gray,
fontSize = 11.sp,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = traceNumber.ifEmpty { "000000" },
color = Color.LegacyRed,
fontSize = 32.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Enter 6-digit trace number",
color = Color.Gray,
fontSize = 11.sp,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Spacer(modifier = Modifier.height(24.dp))
TraceNumberKeypad(
onNumberClick = { value ->
if (traceNumber.length < 6) {
traceNumber = (traceNumber + value).take(6)
}
}
)
Spacer(modifier = Modifier.height(20.dp))
Column(modifier = Modifier.weight(1f)) {
if (transaction != null) {
TransactionDetailsCard(transaction = transaction!!)
}
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 18.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Button(
onClick = onBack,
modifier = Modifier
.weight(1f)
.height(56.dp),
shape = RoundedCornerShape(8.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color.White,
contentColor = Color.LegacyRed
)
) {
Text("Cancel")
}
Button(
onClick = {
searchedTrace = traceNumber
},
modifier = Modifier
.weight(1f)
.height(56.dp),
enabled = traceNumber.isNotBlank(),
shape = RoundedCornerShape(8.dp),
colors = ButtonDefaults.buttonColors(
containerColor = Color.LegacyRed,
contentColor = Color.White,
disabledContainerColor = Color.LegacyRed.copy(alpha = 0.5f),
disabledContentColor = Color.White
)
) {
Text("Enter")
}
}
}
Card(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(top = 110.dp, end = 16.dp)
.clickable(enabled = traceNumber.isNotEmpty()) {
traceNumber = traceNumber.dropLast(1)
},
shape = RoundedCornerShape(18.dp),
colors = CardDefaults.cardColors(containerColor = Color.White),
elevation = CardDefaults.cardElevation(defaultElevation = 6.dp)
) {
androidx.compose.material3.Icon(
imageVector = Icons.Rounded.Backspace,
contentDescription = "Delete",
tint = if (traceNumber.isNotEmpty()) Color.LegacyRed else Color.Gray,
modifier = Modifier.padding(horizontal = 14.dp, vertical = 12.dp)
)
}
}
}
}
@Composable
private fun TraceNumberKeypad(
onNumberClick: (String) -> Unit
) {
val keys = listOf(
listOf("1", "2", "3"),
listOf("4", "5", "6"),
listOf("7", "8", "9"),
listOf("", "0", "00")
)
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
keys.forEach { row ->
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(6.dp)
) {
row.forEach { key ->
if (key.isBlank()) {
Spacer(
modifier = Modifier
.weight(1f)
.height(66.dp)
)
} else {
TraceKeypadButton(
text = key,
modifier = Modifier.weight(1f),
onClick = { onNumberClick(key) }
)
}
}
}
}
}
}
@Composable
private fun TraceKeypadButton(
text: String,
modifier: Modifier = Modifier,
onClick: () -> Unit
) {
Box(
modifier = modifier
.height(66.dp)
.shadow(
elevation = 2.dp,
shape = RoundedCornerShape(8.dp),
clip = false
)
.background(
color = Color.White,
shape = RoundedCornerShape(8.dp)
)
.clickable { onClick() },
contentAlignment = Alignment.Center
) {
Text(
text = text,
color = Color.LegacyRed,
fontSize = 24.sp,
fontWeight = FontWeight.Normal,
textAlign = TextAlign.Center
)
}
}
@Composable
private fun TransactionDetailsCard(transaction: PayDetail) {
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(containerColor = Color.White)
) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Text(
text = "Previous Sale",
color = Color.LegacyRed,
fontWeight = FontWeight.Bold,
fontSize = 16.sp
)
DetailRow("Trace", transaction.voucherNo)
DetailRow("Amount", POSUtil.getInstance().formatAmount(transaction.amount))
DetailRow("Card No", transaction.cardNo)
DetailRow("Reference", transaction.referNo)
DetailRow("Approval", transaction.approvalCode.orEmpty())
DetailRow("Date", transaction.tradeDate)
DetailRow("Time", transaction.tradeTime)
}
}
}
@Composable
private fun DetailRow(
label: String,
value: String
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = label,
color = Color.Gray,
fontSize = 13.sp
)
Text(
text = value.ifBlank { "-" },
color = Color.Black,
fontSize = 13.sp,
fontWeight = FontWeight.Medium
)
}
}

View File

@ -60,7 +60,7 @@ public class DatabaseModule {
private static final String DATABASE_KEY_ALIAS = "RoomDatabaseKeyAlias"; // Alias for the database key in Android Keystore
private static final String ANDROID_KEYSTORE = "AndroidKeyStore";
private static final String DATABASE_NAME = "yoma";
private static final String DATABASE_NAME = "mob";
static final Migration MIGRATION_7_8 = new Migration(7, 8) {
@Override
@ -422,67 +422,6 @@ public class DatabaseModule {
}
})
.build();
// if (INSTANCE == null) {
// synchronized (AppDatabase.class) {
// if (INSTANCE == null) {
// try {
// // Step 1: Try to get the database key
// byte[] databaseKey = getDatabaseKey(application.getApplicationContext());
//
// // Create the SQLCipher factory with the key
// SupportSQLiteOpenHelper.Factory factory = new SupportFactory(databaseKey);
//
// // Try to build and open the database
// try {
// INSTANCE = Room.databaseBuilder(application,
// AppDatabase.class, DATABASE_NAME)
// .openHelperFactory(factory)
// .addMigrations(MIGRATION_7_8)
// .addMigrations(MIGRATION_8_9)
// .addMigrations(MIGRATION_9_10)
// .addMigrations(MIGRATION_10_11)
// .addMigrations(MIGRATION_11_12)
// .allowMainThreadQueries()
// .build();
//
// // Validate the database connection by performing a simple query
// // This will throw an exception if the key is wrong
// INSTANCE.getOpenHelper().getWritableDatabase();
// LogUtil.d("Database", "Database opened successfully with encryption key");
// } catch (Exception e) {
// LogUtil.e("Database", "Error opening encrypted database: " + e.getMessage()+ e);
//
// // The key might be wrong - check if this is a first-time issue or a key mismatch
// if (isDatabaseFileCorruptOrWrongKey(application)) {
// LogUtil.w("Database", "Database file exists but can't be opened with current key. Recreating database.");
//
// // Delete the existing database file
// handleDatabaseRecreation(application);
//
// // Create a new database with the current key
// INSTANCE = Room.databaseBuilder(application,
// AppDatabase.class, DATABASE_NAME)
// .openHelperFactory(factory)
// .addMigrations(MIGRATION_7_8)
// .addMigrations(MIGRATION_8_9)
// .addMigrations(MIGRATION_9_10)
// .addMigrations(MIGRATION_10_11)
// .addMigrations(MIGRATION_11_12)
// .allowMainThreadQueries()
// .build();
// } else {
// // If it's not a key issue, rethrow the exception
// throw e;
// }
// }
// } catch (Exception e) {
// LogUtil.e("Database", "Critical error initializing database: " + e.getMessage()+ e);
// throw new RuntimeException("Failed to initialize the database", e);
// }
// }
// }
// }
// return INSTANCE;
}
@ -527,32 +466,7 @@ public class DatabaseModule {
.putLong("db_recreated_time", System.currentTimeMillis())
.apply();
}
// return Room.databaseBuilder(application, AppDatabase.class,"yoma")
//// .fallbackToDestructiveMigration()
// .addMigrations(MIGRATION_7_8)
// .addMigrations(MIGRATION_8_9)
// .addMigrations(MIGRATION_9_10)
// .addMigrations(MIGRATION_10_11)
// .addMigrations(MIGRATION_11_12)
// .allowMainThreadQueries()
// .addCallback(new RoomDatabase.Callback() {
// @Override
// public void onCreate(@NonNull SupportSQLiteDatabase db) {
// super.onCreate(db);
// Executors.newSingleThreadScheduledExecutor().execute(new Runnable() {
// @Override
// public void run() {
//
// /*
// * init here
// * */
// }
// });
// }
// })
// .build();
// }
@Provides
@Singleton

View File

@ -20,6 +20,7 @@ runtime = "1.11.1"
rxandroid = "3.0.2"
rxjava = "3.1.12"
hiltNavigationCompose = "1.2.0"
runtimeLivedata = "1.11.2"
[libraries]
androidx-compose-material-icons-core = { module = "androidx.compose.material:material-icons-core" }
@ -51,6 +52,7 @@ retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit
rxandroid = { module = "io.reactivex.rxjava3:rxandroid", version.ref = "rxandroid" }
rxjava = { module = "io.reactivex.rxjava3:rxjava", version.ref = "rxjava" }
androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" }
androidx-compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "runtimeLivedata" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }