Compare commits

...

3 Commits

Author SHA1 Message Date
moon
92a24851f1 alert trigger 2026-07-22 10:40:56 +06:30
moon
f1a2707190 setup alert receiver 2026-07-22 00:11:45 +06:30
moon
706a48d1d1 released date added 2026-07-21 23:27:19 +06:30
7 changed files with 248 additions and 4 deletions

View File

@ -7,4 +7,8 @@
* Amount and Card Number show in signature pad * Amount and Card Number show in signature pad
* fixed recent transaction only working for top 3 * fixed recent transaction only working for top 3
* routed to dashboard after printing receipt * routed to dashboard after printing receipt
* receipt printing animation re-scale * receipt printing animation re-scale
*
### version 3.4
* disable display always on
* auto-settlement

View File

@ -19,6 +19,7 @@
<uses-permission android:name="com.sunmi.perm.SECURITY" /> <uses-permission android:name="com.sunmi.perm.SECURITY" />
<uses-permission android:name="com.sunmi.perm.CONTACTLESS_CARD" /> <uses-permission android:name="com.sunmi.perm.CONTACTLESS_CARD" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.CAMERA" />
@ -38,6 +39,8 @@
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
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"/>
<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"
android:exported="true" android:exported="true"

View File

@ -1,26 +1,33 @@
package com.mob.utsmyanmar package com.mob.utsmyanmar
import android.Manifest
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.WindowManager
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.annotation.RequiresApi
import androidx.annotation.RequiresPermission
import androidx.core.view.WindowCompat import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat import androidx.core.view.WindowInsetsControllerCompat
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.mob.utsmyanmar.auto_settlement.SettlementScheduler
import com.mob.utsmyanmar.ui.navigation.AppNavGraph import com.mob.utsmyanmar.ui.navigation.AppNavGraph
import com.mob.utsmyanmar.ui.theme.MOBPOSTheme import com.mob.utsmyanmar.ui.theme.MOBPOSTheme
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint @AndroidEntryPoint
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
@RequiresApi(Build.VERSION_CODES.N)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
SettlementScheduler.schedule(this)
// installSplashScreen() // installSplashScreen()
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
enableEdgeToEdge() enableEdgeToEdge()
hideSystemBars() hideSystemBars()
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) // window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
setContent { setContent {
MOBPOSTheme { MOBPOSTheme {
val navController = rememberNavController() val navController = rememberNavController()

View File

@ -0,0 +1,26 @@
package com.mob.utsmyanmar.auto_settlement
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import androidx.core.content.ContextCompat
import dagger.hilt.android.AndroidEntryPoint
import sunmi.sunmiui.utils.LogUtil
@AndroidEntryPoint
class AlarmReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
val serviceIntent = Intent(context, AutoSettleService::class.java)
LogUtil.d(AlarmReceiver::class.java.simpleName, "Alert service")
if (context != null) {
ContextCompat.startForegroundService(
context,
serviceIntent
)
}
LogUtil.d(AlarmReceiver::class.java.simpleName, "Foreground service")
}
}

View File

@ -0,0 +1,91 @@
package com.mob.utsmyanmar.auto_settlement
import android.annotation.SuppressLint
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.Service
import android.content.Intent
import android.media.AudioAttributes
import android.media.RingtoneManager
import android.os.Build
import android.os.IBinder
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import com.mob.utsmyanmar.MainActivity
import com.mob.utsmyanmar.R
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation
import dagger.hilt.android.AndroidEntryPoint
import sunmi.sunmiui.utils.LogUtil
@AndroidEntryPoint
class AutoSettleService : Service() {
companion object {
const val CHANNEL_ID = "auto_settlement_channel"
const val NOTIFICATION_ID = 1001
}
override fun onCreate() {
super.onCreate()
LogUtil.d(AutoSettleService::class.java.simpleName, "auto settle service called")
startForegroundNotification()
}
@RequiresApi(Build.VERSION_CODES.N)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (!SystemParamsOperation.getInstance().settlementStatus) {
stopSelf()
return START_NOT_STICKY
}
launchSettlement()
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
return START_NOT_STICKY
}
override fun onBind(intent: Intent?): IBinder? = null
@SuppressLint("ForegroundServiceType")
private fun startForegroundNotification() {
LogUtil.d(AutoSettleService::class.java.simpleName, "foreground noti called")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
CHANNEL_ID,
"AUTO SETTLEMENT",
NotificationManager.IMPORTANCE_HIGH
)
channel.description = "Auto Settlement notification"
channel.enableVibration(true)
channel.setSound(
RingtoneManager.getDefaultUri(
RingtoneManager.TYPE_NOTIFICATION
), AudioAttributes.Builder().setUsage(AudioAttributes.USAGE_NOTIFICATION).build()
)
val manager = getSystemService(NotificationManager::class.java)
manager?.createNotificationChannel(channel)
}
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.logo_mob)
.setContentTitle("Auto Settlement")
.setContentText("Processing...")
.setPriority(NotificationCompat.PRIORITY_HIGH)
.build()
startForeground(NOTIFICATION_ID, notification)
}
private fun launchSettlement() {
val intent = Intent(this, MainActivity::class.java).apply {
addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
)
putExtra("AUTO_SETTLEMENT", true)
}
startActivity(intent)
}
}

View File

@ -0,0 +1,84 @@
package com.mob.utsmyanmar.auto_settlement
import android.Manifest
import android.annotation.SuppressLint
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import androidx.annotation.RequiresPermission
import java.util.Calendar
object SettlementScheduler {
@RequiresPermission(Manifest.permission.SCHEDULE_EXACT_ALARM)
fun schedule(context: Context) {
val calendar = Calendar.getInstance().apply {
// Auto settlement time
set(Calendar.HOUR_OF_DAY, 1)
set(Calendar.MINUTE, 10)
set(Calendar.SECOND, 0)
if (before(Calendar.getInstance())) {
add(Calendar.DAY_OF_MONTH, 1)
}
}
val alarmManager =
context.getSystemService(Context.ALARM_SERVICE)
as AlarmManager
val intent = Intent(
context,
AlarmReceiver::class.java
)
val pendingIntent = PendingIntent.getBroadcast(
context,
100,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or
PendingIntent.FLAG_IMMUTABLE
)
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
pendingIntent
)
}
@SuppressLint("ServiceCast")
fun cancel(context: Context) {
val alarmManager =
context.getSystemService(Context.ALARM_SERVICE)
as AlarmManager
val intent = Intent(
context,
AlarmReceiver::class.java
)
val pendingIntent = PendingIntent.getBroadcast(
context,
100,
intent,
PendingIntent.FLAG_UPDATE_CURRENT or
PendingIntent.FLAG_IMMUTABLE
)
alarmManager.cancel(pendingIntent)
}
}

View File

@ -51,6 +51,7 @@ import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import com.mob.utsmyanmar.BuildConfig
import com.mob.utsmyanmar.R import com.mob.utsmyanmar.R
import com.mob.utsmyanmar.ui.components.appbar.AppBar import com.mob.utsmyanmar.ui.components.appbar.AppBar
import com.mob.utsmyanmar.ui.device_info.DeviceInfoUiState import com.mob.utsmyanmar.ui.device_info.DeviceInfoUiState
@ -80,7 +81,7 @@ fun DashboardScreen2(
) { ) {
val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed)
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val mainHandler = remember { Handler(Looper.getMainLooper())} val mainHandler = remember { Handler(Looper.getMainLooper()) }
var showHostActionDialog by remember { mutableStateOf(false) } var showHostActionDialog by remember { mutableStateOf(false) }
var activeHostAction by remember { mutableStateOf("Log-On") } var activeHostAction by remember { mutableStateOf("Log-On") }
var isHostActionRunning by remember { mutableStateOf(false) } var isHostActionRunning by remember { mutableStateOf(false) }
@ -97,6 +98,7 @@ fun DashboardScreen2(
fun successMessage(action: String) = "$action success." fun successMessage(action: String) = "$action success."
fun failureMessage(action: String, resultCode: Int?) = fun failureMessage(action: String, resultCode: Int?) =
"$action failed. Response code: ${resultCode ?: -1}" "$action failed. Response code: ${resultCode ?: -1}"
fun networkFailureMessage(action: String) = "Network error during $action." fun networkFailureMessage(action: String) = "Network error during $action."
fun openHostActionDialog(action: String) { fun openHostActionDialog(action: String) {
activeHostAction = action activeHostAction = action
@ -141,12 +143,14 @@ fun DashboardScreen2(
dialogMessage = successMessage(activeHostAction) dialogMessage = successMessage(activeHostAction)
} }
} }
override fun onFailureSignOn(resultCode: Int?) { override fun onFailureSignOn(resultCode: Int?) {
mainHandler.post { mainHandler.post {
isHostActionRunning = false isHostActionRunning = false
dialogMessage = failureMessage(activeHostAction, resultCode) dialogMessage = failureMessage(activeHostAction, resultCode)
} }
} }
override fun onNetworkFailSignOn(message: String?) { override fun onNetworkFailSignOn(message: String?) {
mainHandler.post { mainHandler.post {
isHostActionRunning = false isHostActionRunning = false
@ -253,6 +257,21 @@ fun DashboardScreen2(
} }
} }
} }
Spacer(modifier = Modifier.height(10.dp))
Text(
text = "App Version : ${BuildConfig.VERSION_NAME}",
style = MaterialTheme.typography.bodySmall,
color = Color.Gray,
modifier = Modifier.padding(horizontal = 16.dp)
)
Spacer(modifier = Modifier.height(10.dp))
Text(
text = "Released on : 21-Jul-26",
style = MaterialTheme.typography.bodySmall,
color = Color.Gray,
modifier = Modifier.padding(horizontal = 16.dp)
)
Spacer(modifier = Modifier.height(10.dp)) Spacer(modifier = Modifier.height(10.dp))
Text( Text(
@ -341,6 +360,16 @@ fun DashboardScreen2(
}, },
trailingIcon = {}, trailingIcon = {},
) )
Spacer(modifier = Modifier.weight(1f))
Text(
text = "v${BuildConfig.VERSION_NAME}",
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
style = MaterialTheme.typography.labelSmall,
color = Color.Black.copy(alpha = 0.5f)
)
} }
} }
) { ) {