Compare commits

..

4 Commits

Author SHA1 Message Date
moon
43c903c9ee fixed continued button in transaction result screen and date time in paper receipt 2026-09-09 23:41:21 +06:30
moon
23a4ebfb64 qr for user manual 2026-09-03 11:25:31 +06:30
moon
962bfbc6c5 auto settlement fixed 2026-08-26 16:17:07 +06:30
moon
2093fb214a i changed something last night but i did not remember 2026-08-26 10:06:20 +06:30
12 changed files with 154 additions and 47 deletions

View File

@ -40,8 +40,8 @@ android {
dimension = "env" dimension = "env"
applicationIdSuffix = ".uat" applicationIdSuffix = ".uat"
versionNameSuffix = "-UAT" versionNameSuffix = "-UAT"
versionCode = 17 versionCode = 19
versionName = "3.5" versionName = "3.7"
buildConfigField("String", "ENV", "\"UAT\"") buildConfigField("String", "ENV", "\"UAT\"")
resValue("string", "app_name", "MOB-UAT") resValue("string", "app_name", "MOB-UAT")
} }

View File

@ -19,3 +19,18 @@
* show reversal status in trans records * show reversal status in trans records
* error_slip_print enable/disable in tms * error_slip_print enable/disable in tms
* auto_settlement enable/disable in tms * auto_settlement enable/disable in tms
### version 3.5
* "always with you" in mob receipt logo
* "..." full width in both P2 and P3 preview screens
* JCB logo added in Card Capture Screen
* "-" in void, refund and cash adv amounts
* disable sale button if amount is 0 or 0.00
* changed date formats to dd/mm/yy to dd/mm/yyyy
* changed keyboard design in reprint any transactions
### version 3.6
* qr for user manual
### version 3.7
* auto-settlement for both P2 and P3

View File

@ -23,6 +23,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" /> <uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
@ -43,6 +44,11 @@
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/Theme.MOBPOS"> android:theme="@style/Theme.MOBPOS">
<receiver android:name=".auto_settlement.AlarmReceiver" android:exported="false"/> <receiver android:name=".auto_settlement.AlarmReceiver" android:exported="false"/>
<receiver android:name=".auto_settlement.BootReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service android:name=".auto_settlement.AutoSettleService" android:exported="false" android:foregroundServiceType="dataSync" /> <service android:name=".auto_settlement.AutoSettleService" android:exported="false" android:foregroundServiceType="dataSync" />
<activity <activity
android:name="com.mob.utsmyanmar.MainActivity" android:name="com.mob.utsmyanmar.MainActivity"

View File

@ -75,7 +75,7 @@ class MainActivity : ComponentActivity() {
private fun scheduleSettlementAlarm() { private fun scheduleSettlementAlarm() {
if (!SystemParamsOperation.getInstance().isAutoSettlement()) { if (!SystemParamsOperation.getInstance().isAutoSettlement()) {
SettlementScheduler.schedule(this) SettlementScheduler.cancel(this)
return return
} }
@ -91,6 +91,7 @@ class MainActivity : ComponentActivity() {
) )
) )
} }
SettlementScheduler.scheduleFallback(this)
return return
} }
} }

View File

@ -0,0 +1,16 @@
package com.mob.utsmyanmar.auto_settlement
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import sunmi.sunmiui.utils.LogUtil
class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action != Intent.ACTION_BOOT_COMPLETED) return
val appContext = context ?: return
LogUtil.d(BootReceiver::class.java.simpleName, "Boot completed, rescheduling settlement alarm")
SettlementScheduler.schedule(appContext)
}
}

View File

@ -11,6 +11,7 @@ import java.util.Calendar
object SettlementScheduler { object SettlementScheduler {
private const val FALLBACK_ALERT_TIME = "23:30:00" private const val FALLBACK_ALERT_TIME = "23:30:00"
private const val REQUEST_CODE = 100
fun schedule(context: Context): Boolean { fun schedule(context: Context): Boolean {
if (!SystemParamsOperation.getInstance().isAutoSettlement()) { if (!SystemParamsOperation.getInstance().isAutoSettlement()) {
@ -24,15 +25,49 @@ object SettlementScheduler {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
!alarmManager.canScheduleExactAlarms() !alarmManager.canScheduleExactAlarms()
) { ) {
return scheduleFallback(context)
}
val calendar = nextAlarmCalendar() ?: return false
val pendingIntent = alarmPendingIntent(context)
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
pendingIntent
)
return true
}
fun scheduleFallback(context: Context): Boolean {
if (!SystemParamsOperation.getInstance().isAutoSettlement()) {
cancel(context)
return false return false
} }
val configuredTime = parseTime( val alarmManager =
// SystemParamsOperation.getInstance().clearBatchTime context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
"21:35:00"
) ?: parseTime(FALLBACK_ALERT_TIME)!!
val calendar = Calendar.getInstance().apply { val calendar = nextAlarmCalendar() ?: return false
val pendingIntent = alarmPendingIntent(context)
alarmManager.setAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
pendingIntent
)
return true
}
private fun nextAlarmCalendar(): Calendar? {
val configuredTime = parseTime(
SystemParamsOperation.getInstance().clearBatchTime
// "15:45:00"
) ?: parseTime(FALLBACK_ALERT_TIME) ?: return null
return Calendar.getInstance().apply {
set(Calendar.HOUR_OF_DAY, configuredTime.first) set(Calendar.HOUR_OF_DAY, configuredTime.first)
set(Calendar.MINUTE, configuredTime.second) set(Calendar.MINUTE, configuredTime.second)
set(Calendar.SECOND, configuredTime.third) set(Calendar.SECOND, configuredTime.third)
@ -42,30 +77,18 @@ object SettlementScheduler {
add(Calendar.DAY_OF_MONTH, 1) add(Calendar.DAY_OF_MONTH, 1)
} }
} }
}
private fun alarmPendingIntent(context: Context): PendingIntent {
val intent = Intent(context, AlarmReceiver::class.java)
val intent = Intent( return PendingIntent.getBroadcast(
context, context,
AlarmReceiver::class.java REQUEST_CODE,
)
val pendingIntent = PendingIntent.getBroadcast(
context,
100,
intent, intent,
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_UPDATE_CURRENT or
PendingIntent.FLAG_IMMUTABLE PendingIntent.FLAG_IMMUTABLE
) )
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
pendingIntent
)
return true
} }
private fun parseTime(value: String): Triple<Int, Int, Int>? { private fun parseTime(value: String): Triple<Int, Int, Int>? {

View File

@ -3,6 +3,8 @@ package com.mob.utsmyanmar.ui.dashboard
import android.os.Build import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import androidmads.library.qrgenearator.QRGContents
import androidmads.library.qrgenearator.QRGEncoder
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@ -46,6 +48,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@ -264,7 +267,7 @@ fun DashboardScreen2(
) )
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
Text( Text(
text = "Released on : 21-Jul-26", text = "Released on : 06-Sep-2026",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = Color.Gray, color = Color.Gray,
modifier = Modifier.padding(horizontal = 16.dp) modifier = Modifier.padding(horizontal = 16.dp)
@ -359,6 +362,38 @@ fun DashboardScreen2(
trailingIcon = {}, trailingIcon = {},
) )
Spacer(modifier = Modifier.height(16.dp))
val adUrl = "https://www.mobmyanmar.com/"
val qrBitmap = remember {
runCatching {
val encoder = QRGEncoder(adUrl, null, QRGContents.Type.TEXT, 256)
encoder.colorBlack = android.graphics.Color.BLACK
encoder.colorWhite = android.graphics.Color.WHITE
encoder.bitmap.asImageBitmap()
}.getOrNull()
}
qrBitmap?.let { bitmap ->
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
) {
Image(
bitmap = bitmap,
contentDescription = "Scan for User Guide",
modifier = Modifier.size(150.dp)
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = "Scan for Offers",
style = MaterialTheme.typography.labelSmall,
color = Color.Gray
)
}
}
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
Text( Text(
text = "v${BuildConfig.VERSION_NAME}", text = "v${BuildConfig.VERSION_NAME}",

View File

@ -436,7 +436,14 @@ fun AppNavGraph(
SettlementScreen( SettlementScreen(
sharedViewModel = sharedViewModel, sharedViewModel = sharedViewModel,
settlementViewMode = settlementViewMode, settlementViewMode = settlementViewMode,
onBack = { navController.popBackStack(Routes.Dashboard.route,false) }, onBack = {
onAutoSettlementHandled()
if (!navController.popBackStack(Routes.Dashboard.route, false)) {
navController.navigate(Routes.Dashboard.route) {
launchSingleTop = true
}
}
},
autoStartSettlement = autoSettlementRequested, autoStartSettlement = autoSettlementRequested,
onStartSettlement = { onStartSettlement = {
sharedViewModel.transactionsType.value = TransactionsType.SETTLEMENT sharedViewModel.transactionsType.value = TransactionsType.SETTLEMENT

View File

@ -87,6 +87,7 @@ fun TransactionResultScreen(
transactionId = state.payDetail?.referNo.toString() transactionId = state.payDetail?.referNo.toString()
) )
if (!state.isSuccess) {
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
Button( Button(
@ -101,6 +102,7 @@ fun TransactionResultScreen(
) { ) {
Text("Continue") Text("Continue")
} }
}
} }
} }

View File

@ -6,5 +6,6 @@ data class TransactionResultState(
val title: String = "Transaction Result", val title: String = "Transaction Result",
val message: String = "", val message: String = "",
val isLoading: Boolean = false, val isLoading: Boolean = false,
val isSuccess: Boolean = false,
val payDetail: PayDetail? = null val payDetail: PayDetail? = null
) )

View File

@ -113,7 +113,8 @@ class TransactionResultViewModel @Inject constructor(
_state.value = _state.value.copy( _state.value = _state.value.copy(
title = title, title = title,
message = message message = message,
isSuccess = isSuccess
) )
} }

View File

@ -895,11 +895,11 @@ public abstract class BaseXPrint {
try { try {
sunmiPrinterService.setFontSize(18, innerResultCallback); sunmiPrinterService.setFontSize(18, innerResultCallback);
sunmiPrinterService.setAlignment(0, innerResultCallback); sunmiPrinterService.setAlignment(0, innerResultCallback);
sunmiPrinterService.printColumnsString(new String[]{"DATE :" + PrintUtils.getInstance().toFullYearDate(payDetail.getTransDate()), "TIME: " + payDetail.getTransTime()}, new int[]{2, 2}, new int[]{0, 2}, innerResultCallback); sunmiPrinterService.printColumnsString(new String[]{"DATE :" + PrintUtils.getInstance().toFullYearDate(payDetail.getTransDate()), "TIME:" + payDetail.getTransTime()}, new int[]{2, 2}, new int[]{0, 2}, innerResultCallback);
if (payDetail.getTransactionType() == TransactionsType.WAVEPAY.value && payDetail.getQrTransStatus() == 1) { if (payDetail.getTransactionType() == TransactionsType.WAVEPAY.value && payDetail.getQrTransStatus() == 1) {
sunmiPrinterService.printColumnsString(new String[]{"TRACE NO :" + traceNum, "INV NO: " + invoiceNo}, new int[]{2, 2}, new int[]{0, 2}, innerResultCallback); sunmiPrinterService.printColumnsString(new String[]{"TRC NO:" + traceNum, "INV NO: " + invoiceNo}, new int[]{2, 2}, new int[]{0, 2}, innerResultCallback);
} else if (!payDetail.getTransType().equals(SETTLEMENT) && !payDetail.getTransType().equals(SUMMARY) && payDetail.getTransactionType() != TransactionsType.WAVEPAY_REFUND.value && payDetail.getTransactionType() != TransactionsType.WAVEPAY.value) { } else if (!payDetail.getTransType().equals(SETTLEMENT) && !payDetail.getTransType().equals(SUMMARY) && payDetail.getTransactionType() != TransactionsType.WAVEPAY_REFUND.value && payDetail.getTransactionType() != TransactionsType.WAVEPAY.value) {
sunmiPrinterService.printColumnsString(new String[]{"BTH NO :" + batchNum, "INV NO: " + invoiceNo}, new int[]{2, 2}, new int[]{0, 2}, innerResultCallback); sunmiPrinterService.printColumnsString(new String[]{"BTH NO:" + batchNum, "INV NO: " + invoiceNo}, new int[]{2, 2}, new int[]{0, 2}, innerResultCallback);
} else if (payDetail.getTransType().equals(SUMMARY)) { } else if (payDetail.getTransType().equals(SUMMARY)) {
sunmiPrinterService.printColumnsString(new String[]{"HOST :" + HOST_NAME_MPU, ""}, new int[]{2, 1}, new int[]{0, 2}, innerResultCallback); sunmiPrinterService.printColumnsString(new String[]{"HOST :" + HOST_NAME_MPU, ""}, new int[]{2, 1}, new int[]{0, 2}, innerResultCallback);
} }