system maintainment alert
This commit is contained in:
parent
0234c05e63
commit
1f3beeb165
@ -71,7 +71,7 @@ fun buildMenuItems(
|
|||||||
modifier = Modifier.height(48.dp)
|
modifier = Modifier.height(48.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) { }
|
) { onNavigateAction("MMQR") }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
add(
|
add(
|
||||||
|
|||||||
@ -5,10 +5,21 @@ import android.os.Build
|
|||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
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.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.mob.utsmyanmar.ui.theme.Color as AppColor
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
@ -74,11 +85,14 @@ fun AppNavGraph(
|
|||||||
val activity = LocalContext.current as ComponentActivity
|
val activity = LocalContext.current as ComponentActivity
|
||||||
val currentBackStackEntry by navController.currentBackStackEntryAsState()
|
val currentBackStackEntry by navController.currentBackStackEntryAsState()
|
||||||
val currentRoute = currentBackStackEntry?.destination?.route
|
val currentRoute = currentBackStackEntry?.destination?.route
|
||||||
|
val tmsSetupViewModel: TmsSetupViewModel = hiltViewModel(activity)
|
||||||
|
val isSyncBlockedGlobal by tmsSetupViewModel.isSyncBlocked.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
LaunchedEffect(autoSettlementRequested, currentRoute) {
|
LaunchedEffect(autoSettlementRequested, currentRoute, isSyncBlockedGlobal) {
|
||||||
if (!autoSettlementRequested ||
|
if (!autoSettlementRequested ||
|
||||||
currentRoute == null ||
|
currentRoute == null ||
|
||||||
currentRoute == Routes.TmsSetup.route
|
currentRoute == Routes.TmsSetup.route ||
|
||||||
|
isSyncBlockedGlobal
|
||||||
) return@LaunchedEffect
|
) return@LaunchedEffect
|
||||||
if (currentRoute == Routes.Settlement.route) {
|
if (currentRoute == Routes.Settlement.route) {
|
||||||
return@LaunchedEffect
|
return@LaunchedEffect
|
||||||
@ -98,12 +112,11 @@ fun AppNavGraph(
|
|||||||
startDestination = Routes.TmsSetup.route
|
startDestination = Routes.TmsSetup.route
|
||||||
) {
|
) {
|
||||||
composable(Routes.TmsSetup.route) {
|
composable(Routes.TmsSetup.route) {
|
||||||
val tmsSetupViewModel: TmsSetupViewModel = hiltViewModel()
|
|
||||||
TmsSetupRoute(
|
TmsSetupRoute(
|
||||||
viewModel = tmsSetupViewModel,
|
viewModel = tmsSetupViewModel,
|
||||||
onNavigateDashboard = {
|
onNavigateDashboard = {
|
||||||
navController.navigate(
|
navController.navigate(
|
||||||
if (autoSettlementRequested) Routes.Settlement.route else Routes.Dashboard.route
|
if (autoSettlementRequested && !isSyncBlockedGlobal) Routes.Settlement.route else Routes.Dashboard.route
|
||||||
) {
|
) {
|
||||||
popUpTo(Routes.TmsSetup.route) {
|
popUpTo(Routes.TmsSetup.route) {
|
||||||
inclusive = true
|
inclusive = true
|
||||||
@ -150,6 +163,9 @@ fun AppNavGraph(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val tmsSetupUiState by tmsSetupViewModel.uiState.collectAsStateWithLifecycle()
|
val tmsSetupUiState by tmsSetupViewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
val isSyncBlocked by tmsSetupViewModel.isSyncBlocked.collectAsStateWithLifecycle()
|
||||||
|
val syncBlockedMessage by tmsSetupViewModel.syncBlockedMessage.collectAsStateWithLifecycle()
|
||||||
|
var showBlockedSaleAlert by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
LaunchedEffect(tmsSetupViewModel) {
|
LaunchedEffect(tmsSetupViewModel) {
|
||||||
tmsSetupViewModel.onSyncCompleted.collect {
|
tmsSetupViewModel.onSyncCompleted.collect {
|
||||||
@ -201,96 +217,109 @@ fun AppNavGraph(
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
onNavigateAmount = { action ->
|
onNavigateAmount = { action ->
|
||||||
when (action) {
|
if (isSyncBlocked) {
|
||||||
"Pre-Auth",
|
showBlockedSaleAlert = true
|
||||||
"Cash Adv",
|
} else {
|
||||||
"Sale" -> {
|
when (action) {
|
||||||
navController.navigate(Routes.Amount.createRoute(action)) {
|
"Pre-Auth",
|
||||||
launchSingleTop = true
|
"Cash Adv",
|
||||||
|
"Sale" -> {
|
||||||
|
navController.navigate(Routes.Amount.createRoute(action)) {
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
"Pre-Auth",
|
|
||||||
"Cash Adv" -> {
|
|
||||||
navController.navigate(Routes.Amount.createRoute(action)) {
|
|
||||||
launchSingleTop = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
navController.navigate(
|
navController.navigate(
|
||||||
Routes.Password.createRoute(
|
Routes.Password.createRoute(
|
||||||
destination = Routes.Amount.createRoute(action),
|
destination = Routes.Amount.createRoute(action),
|
||||||
passwordType = PasswordType.SYSTEM
|
passwordType = PasswordType.SYSTEM
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onNavigateAction = { action ->
|
onNavigateAction = { action ->
|
||||||
when (action) {
|
if (isSyncBlocked) {
|
||||||
"Void" -> {
|
showBlockedSaleAlert = true
|
||||||
sharedViewModel.transactionsType.value = TransactionsType.VOID
|
} else {
|
||||||
navController.navigate(
|
when (action) {
|
||||||
Routes.Password.createRoute(
|
"Void" -> {
|
||||||
destination = Routes.VoidTrace.route,
|
sharedViewModel.transactionsType.value = TransactionsType.VOID
|
||||||
passwordType = PasswordType.SYSTEM
|
navController.navigate(
|
||||||
)
|
Routes.Password.createRoute(
|
||||||
) {
|
destination = Routes.VoidTrace.route,
|
||||||
launchSingleTop = true
|
passwordType = PasswordType.SYSTEM
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
"Pre-Auth",
|
"Pre-Auth",
|
||||||
"Cash Adv" -> {
|
"Cash Adv" -> {
|
||||||
sharedViewModel.transactionsType.value = when (action) {
|
sharedViewModel.transactionsType.value = when (action) {
|
||||||
"Pre-Auth" -> TransactionsType.PRE_AUTH_SALE
|
"Pre-Auth" -> TransactionsType.PRE_AUTH_SALE
|
||||||
else -> TransactionsType.CASH_OUT
|
else -> TransactionsType.CASH_OUT
|
||||||
|
}
|
||||||
|
navController.navigate(Routes.Amount.createRoute(action)) {
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
navController.navigate(Routes.Amount.createRoute(action)) {
|
|
||||||
launchSingleTop = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"Pre-Auth Complete Void" -> {
|
"Pre-Auth Complete Void" -> {
|
||||||
sharedViewModel.transactionsType.value = TransactionsType.PRE_AUTH_COMPLETE_VOID
|
sharedViewModel.transactionsType.value = TransactionsType.PRE_AUTH_COMPLETE_VOID
|
||||||
navController.navigate(
|
navController.navigate(
|
||||||
Routes.Password.createRoute(
|
Routes.Password.createRoute(
|
||||||
destination = Routes.VoidTrace.route,
|
destination = Routes.VoidTrace.route,
|
||||||
passwordType = PasswordType.SYSTEM
|
passwordType = PasswordType.SYSTEM
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
"MMQR" -> {
|
||||||
navController.navigate(
|
}
|
||||||
Routes.Password.createRoute(
|
|
||||||
destination = Routes.Amount.createRoute(action),
|
else -> {
|
||||||
passwordType = PasswordType.SYSTEM
|
navController.navigate(
|
||||||
)
|
Routes.Password.createRoute(
|
||||||
) {
|
destination = Routes.Amount.createRoute(action),
|
||||||
launchSingleTop = true
|
passwordType = PasswordType.SYSTEM
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onNavigateSignOn = {
|
onNavigateSignOn = {
|
||||||
navController.navigate(Routes.SignOn.route) {
|
if (isSyncBlocked) {
|
||||||
launchSingleTop = true
|
showBlockedSaleAlert = true
|
||||||
|
} else {
|
||||||
|
navController.navigate(Routes.SignOn.route) {
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onNavigateSettlement = {
|
onNavigateSettlement = {
|
||||||
navController.navigate(
|
if (isSyncBlocked) {
|
||||||
Routes.Password.createRoute(
|
showBlockedSaleAlert = true
|
||||||
destination = Routes.Settlement.route,
|
} else {
|
||||||
passwordType = PasswordType.SYSTEM
|
navController.navigate(
|
||||||
)
|
Routes.Password.createRoute(
|
||||||
) {
|
destination = Routes.Settlement.route,
|
||||||
launchSingleTop = true
|
passwordType = PasswordType.SYSTEM
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onNavigateVersion = {
|
onNavigateVersion = {
|
||||||
@ -317,61 +346,137 @@ fun AppNavGraph(
|
|||||||
navController.navigate(Routes.History.route)
|
navController.navigate(Routes.History.route)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (showBlockedSaleAlert) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showBlockedSaleAlert = false },
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = "Maintenance Alert",
|
||||||
|
color = AppColor.LegacyRed,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
text = syncBlockedMessage.ifBlank {
|
||||||
|
"Terminal sync is blocked due to an active maintenance window."
|
||||||
|
},
|
||||||
|
color = AppColor.Black,
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = { showBlockedSaleAlert = false }) {
|
||||||
|
Text(
|
||||||
|
text = "OK",
|
||||||
|
color = AppColor.LegacyRed,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
containerColor = AppColor.White,
|
||||||
|
shape = RoundedCornerShape(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
composable(Routes.SeeMore.route) {
|
composable(Routes.SeeMore.route) {
|
||||||
val sharedViewModel: SharedViewModel = hiltViewModel(activity)
|
val sharedViewModel: SharedViewModel = hiltViewModel(activity)
|
||||||
|
val isSyncBlocked by tmsSetupViewModel.isSyncBlocked.collectAsStateWithLifecycle()
|
||||||
|
val syncBlockedMessage by tmsSetupViewModel.syncBlockedMessage.collectAsStateWithLifecycle()
|
||||||
|
var showBlockedSaleAlert by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
SeeMoreScreen(
|
SeeMoreScreen(
|
||||||
onBack = { navController.popBackStack() },
|
onBack = { navController.popBackStack() },
|
||||||
onNavigateAmount = { action ->
|
onNavigateAmount = { action ->
|
||||||
when (action) {
|
if (isSyncBlocked) {
|
||||||
"Void" -> {
|
showBlockedSaleAlert = true
|
||||||
sharedViewModel.transactionsType.value = TransactionsType.VOID
|
} else {
|
||||||
navController.navigate(
|
when (action) {
|
||||||
Routes.Password.createRoute(
|
"Void" -> {
|
||||||
destination = Routes.VoidTrace.route,
|
sharedViewModel.transactionsType.value = TransactionsType.VOID
|
||||||
passwordType = PasswordType.SYSTEM
|
navController.navigate(
|
||||||
)
|
Routes.Password.createRoute(
|
||||||
) {
|
destination = Routes.VoidTrace.route,
|
||||||
launchSingleTop = true
|
passwordType = PasswordType.SYSTEM
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
"Pre-Auth",
|
"Pre-Auth",
|
||||||
"Cash Adv" -> {
|
"Cash Adv" -> {
|
||||||
sharedViewModel.transactionsType.value = when (action) {
|
sharedViewModel.transactionsType.value = when (action) {
|
||||||
"Pre-Auth" -> TransactionsType.PRE_AUTH_SALE
|
"Pre-Auth" -> TransactionsType.PRE_AUTH_SALE
|
||||||
else -> TransactionsType.CASH_OUT
|
else -> TransactionsType.CASH_OUT
|
||||||
|
}
|
||||||
|
navController.navigate(Routes.Amount.createRoute(action)) {
|
||||||
|
launchSingleTop = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
navController.navigate(Routes.Amount.createRoute(action)) {
|
|
||||||
launchSingleTop = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
"Pre-Auth Complete Void" -> {
|
"Pre-Auth Complete Void" -> {
|
||||||
sharedViewModel.transactionsType.value = TransactionsType.PRE_AUTH_COMPLETE_VOID
|
sharedViewModel.transactionsType.value = TransactionsType.PRE_AUTH_COMPLETE_VOID
|
||||||
navController.navigate(
|
navController.navigate(
|
||||||
Routes.Password.createRoute(
|
Routes.Password.createRoute(
|
||||||
destination = Routes.VoidTrace.route,
|
destination = Routes.VoidTrace.route,
|
||||||
passwordType = PasswordType.SYSTEM
|
passwordType = PasswordType.SYSTEM
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
else -> {
|
||||||
else -> {
|
navController.navigate(
|
||||||
navController.navigate(
|
Routes.Password.createRoute(
|
||||||
Routes.Password.createRoute(
|
destination = Routes.Amount.createRoute(action),
|
||||||
destination = Routes.Amount.createRoute(action),
|
passwordType = PasswordType.SYSTEM
|
||||||
passwordType = PasswordType.SYSTEM
|
)
|
||||||
)
|
) {
|
||||||
) {
|
launchSingleTop = true
|
||||||
launchSingleTop = true
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (showBlockedSaleAlert) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showBlockedSaleAlert = false },
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
text = "Maintenance Alert",
|
||||||
|
color = AppColor.LegacyRed,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(
|
||||||
|
text = syncBlockedMessage.ifBlank {
|
||||||
|
"Terminal sync is blocked due to an active maintenance window."
|
||||||
|
},
|
||||||
|
color = AppColor.Black,
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = { showBlockedSaleAlert = false }) {
|
||||||
|
Text(
|
||||||
|
text = "OK",
|
||||||
|
color = AppColor.LegacyRed,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
containerColor = AppColor.White,
|
||||||
|
shape = RoundedCornerShape(16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
composable(Routes.Version.route){
|
composable(Routes.Version.route){
|
||||||
|
|||||||
@ -32,6 +32,9 @@ import kotlinx.coroutines.flow.update
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.json.JSONObject
|
import org.json.JSONObject
|
||||||
import sunmi.sunmiui.utils.LogUtil
|
import sunmi.sunmiui.utils.LogUtil
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.TimeZone
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
import kotlin.time.Duration.Companion.milliseconds
|
||||||
|
|
||||||
@ -40,7 +43,9 @@ data class TmsSetupUiState(
|
|||||||
val statusText: String = "Initializing...",
|
val statusText: String = "Initializing...",
|
||||||
val isError: Boolean = false,
|
val isError: Boolean = false,
|
||||||
val errorMessage: String = "",
|
val errorMessage: String = "",
|
||||||
val isTerminalDisabled: Boolean = false
|
val isTerminalDisabled: Boolean = false,
|
||||||
|
val isSyncBlocked: Boolean = false,
|
||||||
|
val syncBlockedMessage: String = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
@ -49,8 +54,30 @@ class TmsSetupViewModel @Inject constructor(
|
|||||||
private val emvParamOperation: EmvParamOperation,
|
private val emvParamOperation: EmvParamOperation,
|
||||||
@ApplicationContext private val context: Context
|
@ApplicationContext private val context: Context
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
companion object {
|
||||||
|
const val PREF_KEY_SYNC_BLOCKED = "is_sync_blocked"
|
||||||
|
const val PREF_KEY_SYNC_BLOCKED_MSG = "sync_blocked_message"
|
||||||
|
}
|
||||||
|
|
||||||
private val TAG = TmsSetupViewModel::class.simpleName
|
private val TAG = TmsSetupViewModel::class.simpleName
|
||||||
private val _uiState = MutableStateFlow(TmsSetupUiState())
|
private val prefs = context.getSharedPreferences("mobpos_prefs", Context.MODE_PRIVATE)
|
||||||
|
|
||||||
|
private val _isSyncBlocked = MutableStateFlow(
|
||||||
|
prefs.getBoolean(PREF_KEY_SYNC_BLOCKED, false)
|
||||||
|
)
|
||||||
|
val isSyncBlocked: StateFlow<Boolean> = _isSyncBlocked.asStateFlow()
|
||||||
|
|
||||||
|
private val _syncBlockedMessage = MutableStateFlow(
|
||||||
|
prefs.getString(PREF_KEY_SYNC_BLOCKED_MSG, "").orEmpty()
|
||||||
|
)
|
||||||
|
val syncBlockedMessage: StateFlow<String> = _syncBlockedMessage.asStateFlow()
|
||||||
|
|
||||||
|
private val _uiState = MutableStateFlow(
|
||||||
|
TmsSetupUiState(
|
||||||
|
isSyncBlocked = prefs.getBoolean(PREF_KEY_SYNC_BLOCKED, false),
|
||||||
|
syncBlockedMessage = prefs.getString(PREF_KEY_SYNC_BLOCKED_MSG, "").orEmpty()
|
||||||
|
)
|
||||||
|
)
|
||||||
private val _dashboardUiState = MutableStateFlow(DashboardUiState())
|
private val _dashboardUiState = MutableStateFlow(DashboardUiState())
|
||||||
val uiState: StateFlow<TmsSetupUiState> = _uiState.asStateFlow()
|
val uiState: StateFlow<TmsSetupUiState> = _uiState.asStateFlow()
|
||||||
|
|
||||||
@ -100,7 +127,16 @@ class TmsSetupViewModel @Inject constructor(
|
|||||||
.doFinally { requestInProgress = false }
|
.doFinally { requestInProgress = false }
|
||||||
.subscribe(
|
.subscribe(
|
||||||
{ response ->
|
{ response ->
|
||||||
|
prefs.edit {
|
||||||
|
putBoolean(PREF_KEY_SYNC_BLOCKED, false)
|
||||||
|
remove(PREF_KEY_SYNC_BLOCKED_MSG)
|
||||||
|
}
|
||||||
|
_isSyncBlocked.value = false
|
||||||
|
_syncBlockedMessage.value = ""
|
||||||
|
|
||||||
_uiState.update { it.copy(
|
_uiState.update { it.copy(
|
||||||
|
isSyncBlocked = false,
|
||||||
|
syncBlockedMessage = "",
|
||||||
statusText = "Applying configuration...")
|
statusText = "Applying configuration...")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,24 +153,59 @@ class TmsSetupViewModel @Inject constructor(
|
|||||||
onConfigApplied(onSuccess)
|
onConfigApplied(onSuccess)
|
||||||
},
|
},
|
||||||
{ error ->
|
{ error ->
|
||||||
val errorMessage = if (error is retrofit2.HttpException) {
|
var isSyncBlocked = false
|
||||||
|
var errorMessage = error.message.toString()
|
||||||
|
|
||||||
|
if (error is retrofit2.HttpException) {
|
||||||
|
val httpCode = error.code()
|
||||||
try {
|
try {
|
||||||
val body = error.response()?.errorBody()?.string()
|
val body = error.response()?.errorBody()?.string()
|
||||||
if (!body.isNullOrBlank()) {
|
if (!body.isNullOrBlank()) {
|
||||||
JSONObject(body).optString("message", "Unknown server error")
|
val json = JSONObject(body)
|
||||||
|
val code = json.optString("code")
|
||||||
|
val statusCode = json.optInt("statusCode", httpCode)
|
||||||
|
val msg = json.optString("message", "")
|
||||||
|
if (msg.isNotBlank()) {
|
||||||
|
errorMessage = msg
|
||||||
|
}
|
||||||
|
|
||||||
|
if (httpCode == 423 || statusCode == 423 ||
|
||||||
|
code.equals("TERMINAL_SYNC_BLOCKED", ignoreCase = true) ||
|
||||||
|
(msg.contains("blocked", ignoreCase = true) && msg.contains("maintenance", ignoreCase = true))
|
||||||
|
) {
|
||||||
|
isSyncBlocked = true
|
||||||
|
errorMessage = buildAlertDetailsMessage(json, msg.ifBlank { errorMessage })
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
"Empty error response (${error.code()})"
|
if (httpCode == 423) {
|
||||||
|
isSyncBlocked = true
|
||||||
|
}
|
||||||
|
errorMessage = "Empty error response ($httpCode)"
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
"Server error: ${error.code()}"
|
if (httpCode == 423) {
|
||||||
|
isSyncBlocked = true
|
||||||
|
}
|
||||||
|
errorMessage = "Server error: $httpCode"
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
error.message.toString()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isSyncBlocked) {
|
||||||
|
LogUtil.i(TAG, "Terminal sync is blocked due to maintenance window ($errorMessage).")
|
||||||
|
prefs.edit {
|
||||||
|
putBoolean(PREF_KEY_SYNC_BLOCKED, true)
|
||||||
|
putString(PREF_KEY_SYNC_BLOCKED_MSG, errorMessage)
|
||||||
|
}
|
||||||
|
_isSyncBlocked.value = true
|
||||||
|
_syncBlockedMessage.value = errorMessage
|
||||||
|
}
|
||||||
|
|
||||||
_uiState.update {
|
_uiState.update {
|
||||||
it.copy(
|
it.copy(
|
||||||
isError = true,
|
isError = true,
|
||||||
isLoading = false,
|
isLoading = false,
|
||||||
|
isSyncBlocked = isSyncBlocked,
|
||||||
|
syncBlockedMessage = if (isSyncBlocked) errorMessage else "",
|
||||||
statusText = "Download failed!",
|
statusText = "Download failed!",
|
||||||
errorMessage = errorMessage
|
errorMessage = errorMessage
|
||||||
)
|
)
|
||||||
@ -181,12 +252,20 @@ class TmsSetupViewModel @Inject constructor(
|
|||||||
|
|
||||||
val validity = TMSUtil.getInstance().checkParams()
|
val validity = TMSUtil.getInstance().checkParams()
|
||||||
if (validity.status == ValidityStatus.SUCCESS) {
|
if (validity.status == ValidityStatus.SUCCESS) {
|
||||||
context.getSharedPreferences("mobpos_prefs", Context.MODE_PRIVATE)
|
prefs.edit {
|
||||||
.edit {
|
putLong("last_sync_time", System.currentTimeMillis())
|
||||||
putLong("last_sync_time", System.currentTimeMillis())
|
putBoolean(PREF_KEY_SYNC_BLOCKED, false)
|
||||||
}
|
remove(PREF_KEY_SYNC_BLOCKED_MSG)
|
||||||
|
}
|
||||||
|
_isSyncBlocked.value = false
|
||||||
|
_syncBlockedMessage.value = ""
|
||||||
onSuccess()
|
onSuccess()
|
||||||
_uiState.update { it.copy(isLoading = false, statusText = "Ready.") }
|
_uiState.update { it.copy(
|
||||||
|
isLoading = false,
|
||||||
|
isSyncBlocked = false,
|
||||||
|
syncBlockedMessage = "",
|
||||||
|
statusText = "Ready."
|
||||||
|
) }
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
_onSyncCompleted.emit(Unit)
|
_onSyncCompleted.emit(Unit)
|
||||||
_navigateToDashboard.emit(Unit)
|
_navigateToDashboard.emit(Unit)
|
||||||
@ -222,6 +301,65 @@ class TmsSetupViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun buildAlertDetailsMessage(json: JSONObject, defaultMessage: String): String {
|
||||||
|
val builder = StringBuilder()
|
||||||
|
val msg = json.optString("message", defaultMessage).trim()
|
||||||
|
if (msg.isNotEmpty()) {
|
||||||
|
builder.append(msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
val alertsArray = json.optJSONArray("alerts")
|
||||||
|
if (alertsArray != null && alertsArray.length() > 0) {
|
||||||
|
for (i in 0 until alertsArray.length()) {
|
||||||
|
val alert = alertsArray.optJSONObject(i) ?: continue
|
||||||
|
if (builder.isNotEmpty()) {
|
||||||
|
builder.append("\n\n")
|
||||||
|
}
|
||||||
|
val title = alert.optString("title").trim()
|
||||||
|
val desc = alert.optString("description").trim()
|
||||||
|
val type = alert.optString("type").trim()
|
||||||
|
val startAt = alert.optString("startAt").trim()
|
||||||
|
val endAt = alert.optString("endAt").trim()
|
||||||
|
|
||||||
|
val lines = mutableListOf<String>()
|
||||||
|
if (title.isNotEmpty()) lines.add("Title: $title")
|
||||||
|
if (desc.isNotEmpty()) lines.add("Description: $desc")
|
||||||
|
if (type.isNotEmpty()) lines.add("Type: $type")
|
||||||
|
if (startAt.isNotEmpty()) lines.add("Start At: ${formatDateTime(startAt)}")
|
||||||
|
if (endAt.isNotEmpty()) lines.add("End At: ${formatDateTime(endAt)}")
|
||||||
|
|
||||||
|
builder.append(lines.joinToString("\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString().ifBlank { defaultMessage }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatDateTime(isoString: String): String {
|
||||||
|
if (isoString.isBlank()) return ""
|
||||||
|
val patterns = arrayOf(
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'",
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss'Z'",
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss.SSS",
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ss"
|
||||||
|
)
|
||||||
|
for (pattern in patterns) {
|
||||||
|
try {
|
||||||
|
val parser = SimpleDateFormat(pattern, Locale.US).apply {
|
||||||
|
if (pattern.endsWith("'Z'")) {
|
||||||
|
timeZone = TimeZone.getTimeZone("UTC")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val date = parser.parse(isoString)
|
||||||
|
if (date != null) {
|
||||||
|
val formatter = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.getDefault())
|
||||||
|
return formatter.format(date)
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {}
|
||||||
|
}
|
||||||
|
return isoString
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
disposables.clear()
|
disposables.clear()
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
<domain-config cleartextTrafficPermitted="true">
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
<domain includeSubdomains="true">posuat.myanmarorientalbank.com:5033</domain>
|
<domain includeSubdomains="true">posuat.myanmarorientalbank.com:5033</domain>
|
||||||
|
<domain includeSubdomains="true">192.168.10.104</domain>
|
||||||
</domain-config>
|
</domain-config>
|
||||||
|
|
||||||
</network-security-config>
|
</network-security-config>
|
||||||
|
|||||||
@ -38,9 +38,10 @@ public class SystemParamsSettings implements Serializable {
|
|||||||
|
|
||||||
private boolean checkExpSwitch = false;
|
private boolean checkExpSwitch = false;
|
||||||
|
|
||||||
private String tmsAddress = "https://posmgmt.myanmarorientalbank.com/api/v1";
|
// private String tmsAddress = "https://posmgmt.myanmarorientalbank.com/api/v1";
|
||||||
// private String tmsAddress = "https://sirius-nest.utsmyanmar.com/api/v1";
|
// private String tmsAddress = "https://sirius-nest.utsmyanmar.com/api/v1";
|
||||||
// private String tmsAddress = "http://128.199.170.203";
|
// private String tmsAddress = "http://128.199.170.203";
|
||||||
|
private String tmsAddress = "http://192.168.10.104:3333/api/v1";
|
||||||
|
|
||||||
private String terminalCapability = "E0E8C8";
|
private String terminalCapability = "E0E8C8";
|
||||||
private String masterTerminalCapability = "E008C8";
|
private String masterTerminalCapability = "E008C8";
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user