Compare commits

...

2 Commits

7 changed files with 475 additions and 220 deletions

View File

@ -34,6 +34,7 @@ import com.utsmyanmar.baselib.ui.AnimationDialog;
import com.utsmyanmar.checkxread.sdk.NexGoSDK; import com.utsmyanmar.checkxread.sdk.NexGoSDK;
import com.utsmyanmar.ecr.ECRHelper; import com.utsmyanmar.ecr.ECRHelper;
import com.utsmyanmar.paylibs.Constant; import com.utsmyanmar.paylibs.Constant;
import com.utsmyanmar.paylibs.model.PayDetail;
import com.utsmyanmar.paylibs.print.printx.PrintXReceipt; import com.utsmyanmar.paylibs.print.printx.PrintXReceipt;
import com.utsmyanmar.paylibs.utils.POSUtil; import com.utsmyanmar.paylibs.utils.POSUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation; import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
@ -51,6 +52,7 @@ import com.utsmm.kbz.util.tms.TMSUtil;
import java.util.Calendar; import java.util.Calendar;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -414,17 +416,55 @@ public class MainActivity extends AppCompatActivity implements
private void handleAutoSettlementIntent(Intent intent) { private void handleAutoSettlementIntent(Intent intent) {
if (intent == null) return; if (intent == null) return;
// Handle regular auto settlement
boolean auto = intent.getBooleanExtra("AUTO_SETTLEMENT", false); boolean auto = intent.getBooleanExtra("AUTO_SETTLEMENT", false);
if (!auto) return; if (auto) {
com.utsmyanmar.paylibs.model.PayDetail payDetail = (com.utsmyanmar.paylibs.model.PayDetail) intent.getSerializableExtra("EXTRA_PAY_DETAIL");
if (payDetail != null) {
sharedViewModel.payDetail.setValue(payDetail);
sharedViewModel.transactionsType.setValue(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT);
try {
navController.navigate(R.id.transactionResultFragment);
} catch (Exception e) {
LogUtil.e(TAG, "Navigation error: " + e.getMessage());
}
}
return;
}
// Handle QR auto settlement
boolean autoQR = intent.getBooleanExtra("AUTO_QR_SETTLEMENT", false);
if (autoQR) {
int qrSaleCount = intent.getIntExtra("QR_SALE_COUNT", 0);
long qrSaleAmount = intent.getLongExtra("QR_SALE_AMOUNT", 0L);
int qrRefundCount = intent.getIntExtra("QR_REFUND_COUNT", 0);
long qrRefundAmount = intent.getLongExtra("QR_REFUND_AMOUNT", 0L);
long totalAmount = intent.getLongExtra("QR_TOTAL_AMOUNT", 0L);
List<PayDetail> qrTransList = (List<PayDetail>) intent.getSerializableExtra("QR_TRANS_LIST");
com.utsmyanmar.paylibs.model.PayDetail payDetail = (com.utsmyanmar.paylibs.model.PayDetail) intent.getSerializableExtra("EXTRA_PAY_DETAIL"); // Create QR settlement PayDetail
if (payDetail != null) { com.utsmyanmar.paylibs.model.SettleData settleData = new com.utsmyanmar.paylibs.model.SettleData(
qrSaleCount, qrSaleAmount, 0, 0L, qrRefundCount, qrRefundAmount, 0, 0L);
com.utsmyanmar.paylibs.model.TradeData tradeData = com.utsmyanmar.paylibs.utils.params.Params.newTrade(false);
com.utsmyanmar.paylibs.model.PayDetail payDetail = tradeData.getPayDetail();
payDetail.setSettleDataObj(settleData);
payDetail.setTransactionType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT.value);
payDetail.setTransType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT.name);
payDetail.setAmount(totalAmount);
payDetail.setTradeAnswerCode("000");
// Note: QR transactions list is not passed to avoid serialization issues
// The settlement summary is sufficient for the receipt
sharedViewModel.payDetails.setValue(qrTransList);
sharedViewModel.payDetail.setValue(payDetail); sharedViewModel.payDetail.setValue(payDetail);
sharedViewModel.transactionsType.setValue(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT); sharedViewModel.transactionsType.setValue(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT);
try { try {
navController.navigate(R.id.transactionResultFragment); navController.navigate(R.id.transactionResultFragment);
} catch (Exception e) { } catch (Exception e) {
LogUtil.e(TAG, "Navigation error: " + e.getMessage()); LogUtil.e(TAG, "QR Auto Settlement navigation error: " + e.getMessage());
} }
} }
} }

View File

@ -145,7 +145,7 @@ public class MainFragment extends DataBindingFragment {
delayFunctionCall(()-> { delayFunctionCall(()-> {
NexGoSDK.getInstance().cancelCheckCard(); NexGoSDK.getInstance().cancelCheckCard();
NexGoSDK.getInstance().closeReader(); NexGoSDK.getInstance().closeReader();
disableHomeButton(); enableHomeButton();
disableTaskButton(); disableTaskButton();
BaseApplication.getInstance().deviceEngine.getPlatform().hideNavigationBar(); BaseApplication.getInstance().deviceEngine.getPlatform().hideNavigationBar();
}); });
@ -194,14 +194,8 @@ public class MainFragment extends DataBindingFragment {
} }
private void checkDownload() { private void checkDownload() {
Log.d(TAG, "Calling from Check download: "); Log.d(TAG, "Calling from Check download: ");
if (!SystemParamsOperation.getInstance().isDownloadedParams() && SystemParamsOperation.getInstance().isActive() && tmsProcessViewModel.getIsCalled().getValue() == null) { if (!SystemParamsOperation.getInstance().isDownloadedParams() && SystemParamsOperation.getInstance().isActive() && tmsProcessViewModel.getIsCalled().getValue() == null) {
@ -244,7 +238,7 @@ public class MainFragment extends DataBindingFragment {
sharedViewModel.setManualEntryStatus(SystemParamsOperation.getInstance().getManualEntryStatus()); sharedViewModel.setManualEntryStatus(SystemParamsOperation.getInstance().getManualEntryStatus());
// generateMockQR(); generateMockQR();
} }
/* /*
@ -831,15 +825,15 @@ public class MainFragment extends DataBindingFragment {
} }
// private void generateMockQR() { private void generateMockQR() {
// String transDate = "23/12/25"; String transDate = "24/12/25";
// String transTime = "12:06:30"; String transTime = "12:06:30";
// TradeData tradeData = MockData.getInstance().generateMockDataWithTime(TransactionsType.MMQR, 1,transDate,transTime); TradeData tradeData = MockData.getInstance().generateMockDataWithTime(TransactionsType.MMQR, 1,transDate,transTime);
// PayDetail payDetail = tradeData.getPayDetail(); PayDetail payDetail = tradeData.getPayDetail();
// sharedViewModel.insertPayDetail(payDetail); sharedViewModel.insertPayDetail(payDetail);
// LogUtil.d(TAG,"------------------- Inserted Mocked Data ------------------"); LogUtil.d(TAG,"------------------- Inserted Mocked Data ------------------");
// LogUtil.d(TAG,"trans date : "+payDetail.getTransDate() +"- Trans Time: "+payDetail.getTransTime()); LogUtil.d(TAG,"trans date : "+payDetail.getTransDate() +"- Trans Time: "+payDetail.getTransTime());
// } }
public class ClickEvent { public class ClickEvent {

View File

@ -1,10 +1,15 @@
package com.utsmm.kbz.service; package com.utsmm.kbz.service;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import com.utsmyanmar.paylibs.utils.LogUtil; import com.utsmyanmar.paylibs.utils.LogUtil;
import com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation;
import java.util.Calendar;
public class AutoAlarmReceiver extends BroadcastReceiver { public class AutoAlarmReceiver extends BroadcastReceiver {
@ -12,17 +17,84 @@ public class AutoAlarmReceiver extends BroadcastReceiver {
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
LogUtil.d(TAG, "AutoAlarmReceiver triggered at: " + new java.util.Date().toString());
boolean isTestAlarm = intent.getBooleanExtra("TEST_ALARM", false); scheduleNextDayAlarm(context);
if (isTestAlarm) { try {
LogUtil.d(TAG, "This is a test alarm - AutoAlarmReceiver is working correctly!"); Intent serviceIntent = new Intent(context, AutoSettleService.class);
context.startService(serviceIntent);
LogUtil.d(TAG, "AutoSettleService started successfully");
} catch (Exception e) {
LogUtil.e(TAG, "Failed to start AutoSettleService: " + e.getMessage());
e.printStackTrace();
} }
}
// Start the service to send the API request private void scheduleNextDayAlarm(Context context) {
Intent serviceIntent = new Intent(context, AutoSettleService.class); try {
context.startService(serviceIntent); String timeStr = SystemParamsOperation.getInstance().getClearBatchTime();
LogUtil.d(TAG, " Rescheduling next day's alarm for timeStr: " + timeStr);
LogUtil.d(TAG, "AutoSettleService started");
if (timeStr == null || timeStr.trim().isEmpty()) {
LogUtil.d(TAG, "Clear batch time is empty, cannot reschedule");
return;
}
String[] parts = timeStr.trim().split(":");
if (parts.length != 2) {
LogUtil.d(TAG, "Invalid time format: " + timeStr);
return;
}
int hour = Integer.parseInt(parts[0]);
int minute = Integer.parseInt(parts[1]);
// Validate time range
if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
LogUtil.d(TAG, "Invalid time values - hour: " + hour + ", minute: " + minute);
return;
}
// Set up next day's alarm
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.DAY_OF_YEAR, 1); // Next day
calendar.set(Calendar.HOUR_OF_DAY, hour);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
long nextTriggerTime = calendar.getTimeInMillis();
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmManager == null) {
LogUtil.e(TAG, "AlarmManager is null, cannot reschedule");
return;
}
Intent intent = new Intent(context, AutoAlarmReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
1001,
intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// Schedule next day's alarm
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
if (alarmManager.canScheduleExactAlarms()) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
} else {
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
}
} else {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
}
} else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent);
}
} catch (Exception e) {
e.printStackTrace();
}
} }
} }

View File

@ -11,6 +11,7 @@ import android.text.TextUtils;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat; import androidx.core.app.NotificationCompat;
import androidx.lifecycle.Observer;
import androidx.localbroadcastmanager.content.LocalBroadcastManager; import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import com.utsmyanmar.baselib.repo.Repository; import com.utsmyanmar.baselib.repo.Repository;
@ -20,7 +21,6 @@ import com.utsmm.kbz.ui.settlement.SettlementViewModel;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.hilt.android.AndroidEntryPoint; import dagger.hilt.android.AndroidEntryPoint;
import com.utsmyanmar.paylibs.isobuilder.builderx.ISOMsgX; import com.utsmyanmar.paylibs.isobuilder.builderx.ISOMsgX;
@ -35,28 +35,34 @@ public class AutoSettleService extends Service {
public static final String ACTION_DATA_RECEIVED = BuildConfig.APPLICATION_ID + ".ACTION_DATA_RECEIVED"; public static final String ACTION_DATA_RECEIVED = BuildConfig.APPLICATION_ID + ".ACTION_DATA_RECEIVED";
public static final String EXTRA_DATA = BuildConfig.APPLICATION_ID + ".EXTRA_DATA"; public static final String EXTRA_DATA = BuildConfig.APPLICATION_ID + ".EXTRA_DATA";
private Handler handler; private Handler handler;
private SettlementViewModel settlementViewModel; private SettlementViewModel settlementViewModel;
// Flags to prevent infinite loops
private boolean regularSettlementCompleted = false;
private boolean qrSettlementCompleted = false;
// Observers to keep reference for cleanup
private Observer<java.util.List<com.utsmyanmar.paylibs.model.PayDetail>> regularSettlementObserver;
private Observer<java.util.List<com.utsmyanmar.paylibs.model.PayDetail>> qrSettlementObserver;
@Inject @Inject
Repository repository; Repository repository;
public static final String NOTIFICATION_CHANNEL_ID = "10001"; public static final String NOTIFICATION_CHANNEL_ID = "10001";
private final static String default_notification_channel_id = "default"; private final static String default_notification_channel_id = "default";
private void createNotification() { private void createNotification() {
NotificationManager mNotificationManager = (NotificationManager)getSystemService( NOTIFICATION_SERVICE ) ; NotificationManager mNotificationManager = (NotificationManager)getSystemService( NOTIFICATION_SERVICE ) ;
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext() , default_notification_channel_id ) ; NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(getApplicationContext() , default_notification_channel_id ) ;
mBuilder.setContentTitle( "Alarm manager is running!" ) ; mBuilder.setContentTitle( "Auto Settlement Running" ) ;
mBuilder.setContentText( "Wiping out the transactions!" ) ; mBuilder.setContentText( "Processing transactions..." ) ;
mBuilder.setTicker( "Notification Listener Service Example" ) ; mBuilder.setTicker( "Auto Settlement Service" ) ;
mBuilder.setSmallIcon(R.drawable. ic_launcher_foreground ) ; mBuilder.setSmallIcon(R.drawable. ic_launcher_foreground ) ;
mBuilder.setAutoCancel( true ) ; mBuilder.setAutoCancel( true ) ;
if (android.os.Build.VERSION. SDK_INT >= android.os.Build.VERSION_CODES. O ) { if (android.os.Build.VERSION. SDK_INT >= android.os.Build.VERSION_CODES. O ) {
int importance = NotificationManager. IMPORTANCE_HIGH ; int importance = NotificationManager. IMPORTANCE_HIGH ;
NotificationChannel notificationChannel = new NotificationChannel( NOTIFICATION_CHANNEL_ID , "NOTIFICATION_CHANNEL_NAME" , importance) ; NotificationChannel notificationChannel = new NotificationChannel( NOTIFICATION_CHANNEL_ID , "AUTO_SETTLEMENT_CHANNEL" , importance) ;
mBuilder.setChannelId( NOTIFICATION_CHANNEL_ID ) ; mBuilder.setChannelId( NOTIFICATION_CHANNEL_ID ) ;
assert mNotificationManager != null; assert mNotificationManager != null;
mNotificationManager.createNotificationChannel(notificationChannel) ; mNotificationManager.createNotificationChannel(notificationChannel) ;
@ -74,15 +80,16 @@ public class AutoSettleService extends Service {
@SuppressLint("CheckResult") @SuppressLint("CheckResult")
@Override @Override
public int onStartCommand(Intent intent, int flags, int startId) { public int onStartCommand(Intent intent, int flags, int startId) {
// Perform the Settlement request here
LogUtil.d(TAG, "<<<<<<<<<<<<<<< Auto Settlement Service Started >>>>>>>>>>>>>>>>."); LogUtil.d(TAG, "<<<<<<<<<<<<<<< Auto Settlement Service Started >>>>>>>>>>>>>>>>.");
try { try {
sendDataToViewModel("Hello there"); sendDataToViewModel("Auto settlement initiated");
createNotification(); createNotification();
if (SystemParamsOperation.getInstance().getSettlementStatus()) { if (SystemParamsOperation.getInstance().getSettlementStatus()) {
performSettlement(); // performSettlement();
regularSettlementCompleted = true;
performQRSettlement();
} else { } else {
LogUtil.d(TAG, "Settlement is disabled in system parameters"); LogUtil.d(TAG, "Settlement is disabled in system parameters");
stopSelf(); stopSelf();
@ -97,175 +104,315 @@ public class AutoSettleService extends Service {
} }
private void performSettlement() { private void performSettlement() {
if (regularSettlementCompleted) {
return;
}
final ISOMsgX isoMsgX = new ISOMsgX.ISOMsgXBuilder( final ISOMsgX isoMsgX = new ISOMsgX.ISOMsgXBuilder(
com.utsmyanmar.paylibs.isobuilder.builderx.ISOVersion.VERSION_1993, com.utsmyanmar.paylibs.isobuilder.builderx.ISOVersion.VERSION_1993,
com.utsmyanmar.paylibs.isobuilder.ISOMode.BOTH_HEADER_TPDU, com.utsmyanmar.paylibs.isobuilder.ISOMode.BOTH_HEADER_TPDU,
com.utsmyanmar.paylibs.utils.enums.HostName.BPC com.utsmyanmar.paylibs.utils.enums.HostName.BPC
).build(); ).build();
repository.getSettlementPOS().observeForever(list -> { regularSettlementObserver = new Observer<java.util.List<com.utsmyanmar.paylibs.model.PayDetail>>() {
int saleCount = 0; @Override
long saleAmount = 0L; public void onChanged(java.util.List<com.utsmyanmar.paylibs.model.PayDetail> list) {
int preCount = 0; if (regularSettlementCompleted) {
long preAmount = 0L; return;
int refundCount = 0; }
long refundAmount = 0L; regularSettlementCompleted = true;
int caCount = 0;
long caAmount = 0L; // Remove observer immediately to prevent infinite loop
repository.getSettlementPOS().removeObserver(this);
if (list != null && !list.isEmpty()) { int saleCount = 0;
for (com.utsmyanmar.paylibs.model.PayDetail pay : list) { long saleAmount = 0L;
if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.SALE && !pay.isCanceled) { int preCount = 0;
saleCount++; long preAmount = 0L;
saleAmount += pay.getAmount(); int refundCount = 0;
} else if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.PRE_SALE_COMPLETE && !pay.isCanceled) { long refundAmount = 0L;
preCount++; int caCount = 0;
preAmount += pay.getAmount(); long caAmount = 0L;
} else if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.REFUND) {
refundCount++; if (list != null && !list.isEmpty()) {
refundAmount += pay.getAmount(); for (com.utsmyanmar.paylibs.model.PayDetail pay : list) {
} else if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.CASH_ADVANCE) { if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.SALE && !pay.isCanceled) {
caCount++; saleCount++;
caAmount += pay.getAmount(); saleAmount += pay.getAmount();
} else if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.PRE_SALE_COMPLETE && !pay.isCanceled) {
preCount++;
preAmount += pay.getAmount();
} else if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.REFUND) {
refundCount++;
refundAmount += pay.getAmount();
} else if (pay.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.CASH_ADVANCE) {
caCount++;
caAmount += pay.getAmount();
}
} }
} }
}
com.utsmyanmar.paylibs.model.TradeData tradeData = com.utsmyanmar.paylibs.utils.params.Params.newTrade(true); com.utsmyanmar.paylibs.model.TradeData tradeData = com.utsmyanmar.paylibs.utils.params.Params.newTrade(true);
com.utsmyanmar.paylibs.model.PayDetail payDetail = tradeData.getPayDetail(); com.utsmyanmar.paylibs.model.PayDetail payDetail = tradeData.getPayDetail();
String bitmap = com.utsmyanmar.paylibs.utils.iso_utils.BitmapConfig.BPC_SETTLEMENT; String bitmap = com.utsmyanmar.paylibs.utils.iso_utils.BitmapConfig.BPC_SETTLEMENT;
payDetail.setTransType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.name); payDetail.setTransType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.name);
payDetail.setTransactionType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.SETTLEMENT); payDetail.setTransactionType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionType.SETTLEMENT);
payDetail.setProcessCode(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.processCode); payDetail.setProcessCode(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.processCode);
payDetail.setBatchNo(com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation.getInstance().getCurrentBatchNum()); payDetail.setBatchNo(com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation.getInstance().getCurrentBatchNum());
com.utsmyanmar.paylibs.model.SettleData settleData = new com.utsmyanmar.paylibs.model.SettleData( com.utsmyanmar.paylibs.model.SettleData settleData = new com.utsmyanmar.paylibs.model.SettleData(
saleCount, saleCount, saleAmount, preCount, preAmount, refundCount, refundAmount, caCount, caAmount);
saleAmount, payDetail.setSettleDataObj(settleData);
preCount,
preAmount,
refundCount,
refundAmount,
caCount,
caAmount
);
payDetail.setSettleDataObj(settleData);
long totalAmount = saleAmount + preAmount + refundAmount + caAmount; long totalAmount = saleAmount + preAmount + refundAmount + caAmount;
String settlementData; String settlementData;
if (refundAmount != 0L) { if (refundAmount != 0L) {
long creditTotal = saleAmount + preAmount + caAmount; long creditTotal = saleAmount + preAmount + caAmount;
long subTotal = creditTotal - refundAmount; long subTotal = creditTotal - refundAmount;
if (subTotal < 0L) { if (subTotal < 0L) {
settlementData = "D" + String.format(java.util.Locale.getDefault(), "%012d", Math.abs(subTotal)); settlementData = "D" + String.format(java.util.Locale.getDefault(), "%012d", Math.abs(subTotal));
} else {
settlementData = "C" + String.format(java.util.Locale.getDefault(), "%012d", subTotal);
}
} else { } else {
settlementData = "C" + String.format(java.util.Locale.getDefault(), "%012d", subTotal); settlementData = "C" + String.format(java.util.Locale.getDefault(), "%012d", totalAmount);
} }
} else { payDetail.setSettleData(settlementData);
settlementData = "C" + String.format(java.util.Locale.getDefault(), "%012d", totalAmount); payDetail.setAmount(totalAmount);
}
payDetail.setSettleData(settlementData);
payDetail.setAmount(totalAmount);
tradeData.setPayDetail(payDetail); tradeData.setPayDetail(payDetail);
tradeData.setField60(com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation.getInstance().getCurrentBatchNum()); tradeData.setField60(com.utsmyanmar.paylibs.utils.core_utils.SystemParamsOperation.getInstance().getCurrentBatchNum());
byte[] sendBytes = isoMsgX.buildISOPackets(tradeData, bitmap, com.utsmyanmar.paylibs.utils.MessageType.SETTLEMENT);
com.utsmyanmar.paylibs.network.ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length, false, new com.utsmyanmar.paylibs.network.ISOCallback() {
@Override
public void onReceive(byte[] bytes, int length) {
java.util.Map<String, com.utsmyanmar.paylibs.model.MsgField> responseMap = isoMsgX.parseISOPackets(bytes, length);
if (responseMap != null) {
String resultStr = "";
try {
resultStr = responseMap.get("F039").getDataStr();
} catch (NullPointerException e) {
e.printStackTrace();
payDetail.setIsNeedReversal(true);
return;
}
payDetail.setTradeAnswerCode(resultStr);
if (TextUtils.equals(resultStr, com.utsmyanmar.paylibs.Constant.ANSWER_CODE_ACCEPT) || TextUtils.equals(resultStr, com.utsmyanmar.paylibs.Constant.ANSWER_CODE_APPROVED)) {
payDetail.setIsNeedReversal(false);
} else if (TextUtils.equals(resultStr, "95") || TextUtils.equals(resultStr, "095")) {
payDetail.setIsNeedReversal(true);
}
}
}
@Override
public void onError(String msg) {
com.utsmyanmar.paylibs.network.ISOSocket.getInstance().switchIp();
com.utsmyanmar.paylibs.network.ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length, false, this);
}
@Override
public void onComplete() {
LogUtil.d(TAG, "Auto settlement transaction completed successfully");
if (list != null && !list.isEmpty()) {
for (com.utsmyanmar.paylibs.model.PayDetail p : list) {
repository.deletePayDetail(p);
}
}
repository.insertPayDetail(payDetail);
byte[] sendBytes = isoMsgX.buildISOPackets(tradeData, bitmap, com.utsmyanmar.paylibs.utils.MessageType.SETTLEMENT);
com.utsmyanmar.paylibs.network.ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length, false, new com.utsmyanmar.paylibs.network.ISOCallback() {
@Override
public void onReceive(byte[] bytes, int length) {
java.util.Map<String, com.utsmyanmar.paylibs.model.MsgField> responseMap = isoMsgX.parseISOPackets(bytes, length);
if (responseMap != null) {
String resultStr = "";
try { try {
resultStr = responseMap.get("F039").getDataStr(); com.utsmyanmar.paylibs.model.PayDetail cleanPayDetail = new com.utsmyanmar.paylibs.model.PayDetail();
} catch (NullPointerException e) { cleanPayDetail.setTransType(payDetail.getTransType());
cleanPayDetail.setTransactionType(payDetail.getTransactionType());
cleanPayDetail.setProcessCode(payDetail.getProcessCode());
cleanPayDetail.setBatchNo(payDetail.getBatchNo());
cleanPayDetail.setSettleData(payDetail.getSettleData());
cleanPayDetail.setAmount(payDetail.getAmount());
cleanPayDetail.setTradeAnswerCode(payDetail.getTradeAnswerCode());
cleanPayDetail.setIsNeedReversal(payDetail.getIsNeedReversal());
Intent uiIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class);
uiIntent.putExtra("AUTO_SETTLEMENT", true);
uiIntent.putExtra("EXTRA_TRANSACTION_TYPE", com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.value);
uiIntent.putExtra("EXTRA_PAY_DETAIL", cleanPayDetail);
uiIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(uiIntent);
LogUtil.d(TAG, "MainActivity started successfully with auto settlement result");
} catch (Exception e) {
LogUtil.e(TAG, "Error starting MainActivity: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
payDetail.setIsNeedReversal(true);
return; try {
} Intent fallbackIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class);
payDetail.setTradeAnswerCode(resultStr); fallbackIntent.putExtra("AUTO_SETTLEMENT", true);
if (TextUtils.equals(resultStr, com.utsmyanmar.paylibs.Constant.ANSWER_CODE_ACCEPT) || TextUtils.equals(resultStr, com.utsmyanmar.paylibs.Constant.ANSWER_CODE_APPROVED)) { fallbackIntent.putExtra("EXTRA_TRANSACTION_TYPE", com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.value);
payDetail.setIsNeedReversal(false); fallbackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
} else if (TextUtils.equals(resultStr, "95") || TextUtils.equals(resultStr, "095")) { startActivity(fallbackIntent);
payDetail.setIsNeedReversal(true);
LogUtil.d(TAG, "MainActivity started with fallback approach");
} catch (Exception fallbackException) {
LogUtil.e(TAG, "Fallback also failed: " + fallbackException.getMessage());
}
} }
checkIfBothSettlementsComplete();
} }
});
}
};
repository.getSettlementPOS().observeForever(regularSettlementObserver);
}
private void performQRSettlement() {
if (qrSettlementCompleted) {
return;
}
qrSettlementObserver = new Observer<java.util.List<com.utsmyanmar.paylibs.model.PayDetail>>() {
@Override
public void onChanged(java.util.List<com.utsmyanmar.paylibs.model.PayDetail> payDetailList) {
if (qrSettlementCompleted) {
return;
} }
qrSettlementCompleted = true;
@Override
public void onError(String msg) { // Remove observer immediately to prevent infinite loop
com.utsmyanmar.paylibs.network.ISOSocket.getInstance().switchIp(); repository.getTransactionHistory().removeObserver(this);
com.utsmyanmar.paylibs.network.ISOSocket.getInstance().enqueue(sendBytes, sendBytes.length, false, this);
} if (payDetailList != null) {
int qrSaleCount = 0;
@Override long qrSaleAmount = 0;
public void onComplete() { int qrRefundCount = 0;
LogUtil.d(TAG, "Auto settlement transaction completed successfully"); long qrRefundAmount = 0;
if (list != null && !list.isEmpty()) { java.util.ArrayList<com.utsmyanmar.paylibs.model.PayDetail> qrTransactionsList = new java.util.ArrayList<>();
for (com.utsmyanmar.paylibs.model.PayDetail p : list) { java.util.ArrayList<com.utsmyanmar.paylibs.model.PayDetail> qrTransListAll = new java.util.ArrayList<>();
repository.deletePayDetail(p);
}
}
repository.insertPayDetail(payDetail);
try { try {
// Remove the non-serializable SettleData object before passing through Intent for (com.utsmyanmar.paylibs.model.PayDetail payDetail : payDetailList) {
// Create a clean copy of PayDetail without the problematic SettleData object // Filter for successful QR transactions only
com.utsmyanmar.paylibs.model.PayDetail cleanPayDetail = new com.utsmyanmar.paylibs.model.PayDetail(); if ((payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR.value
cleanPayDetail.setTransType(payDetail.getTransType()); || payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_REFUND.value)
cleanPayDetail.setTransactionType(payDetail.getTransactionType()); && payDetail.getQrTransStatus() == 1) {
cleanPayDetail.setProcessCode(payDetail.getProcessCode());
cleanPayDetail.setBatchNo(payDetail.getBatchNo());
cleanPayDetail.setSettleData(payDetail.getSettleData());
cleanPayDetail.setAmount(payDetail.getAmount());
cleanPayDetail.setTradeAnswerCode(payDetail.getTradeAnswerCode());
cleanPayDetail.setIsNeedReversal(payDetail.getIsNeedReversal());
// Note: Not setting SettleDataObj as it's not serializable
Intent uiIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class); qrTransactionsList.add(payDetail);
uiIntent.putExtra("AUTO_SETTLEMENT", true);
uiIntent.putExtra("EXTRA_TRANSACTION_TYPE", com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.value); if (payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR.value) {
uiIntent.putExtra("EXTRA_PAY_DETAIL", cleanPayDetail); qrSaleCount++;
uiIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); qrSaleAmount += payDetail.getAmount();
startActivity(uiIntent); } else if (payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_REFUND.value) {
qrRefundCount++;
LogUtil.d(TAG, "MainActivity started successfully with auto settlement result"); qrRefundAmount += payDetail.getAmount();
}
} catch (Exception e) { } else if (payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR.value
LogUtil.e(TAG, "Error starting MainActivity: " + e.getMessage()); || payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_REFUND.value) {
e.printStackTrace(); // Include all QR transactions for deletion (successful and failed)
qrTransListAll.add(payDetail);
// Fallback: start MainActivity without the PayDetail extra }
try {
Intent fallbackIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class);
fallbackIntent.putExtra("AUTO_SETTLEMENT", true);
fallbackIntent.putExtra("EXTRA_TRANSACTION_TYPE", com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.SETTLEMENT.value);
fallbackIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(fallbackIntent);
LogUtil.d(TAG, "MainActivity started with fallback approach");
} catch (Exception fallbackException) {
LogUtil.e(TAG, "Fallback also failed: " + fallbackException.getMessage());
} }
} catch (IllegalStateException e) {
LogUtil.e(TAG, "QR Auto Settlement: Database cursor error - likely due to large data size: " + e);
checkIfBothSettlementsComplete();
return;
} }
// Stop the service if (!qrTransactionsList.isEmpty()) {
stopSelf(); LogUtil.d(TAG, "Processing QR Auto Settlement with " + qrTransactionsList.size() + " transactions");
long totalAmount = qrSaleAmount - qrRefundAmount;
// Increment batch number for QR settlement
SystemParamsOperation.getInstance().getIncrementBatchNo();
// Create settlement data for QR transactions
com.utsmyanmar.paylibs.model.SettleData settleData = new com.utsmyanmar.paylibs.model.SettleData(
qrSaleCount, qrSaleAmount, 0, 0L, qrRefundCount, qrRefundAmount, 0, 0L);
com.utsmyanmar.paylibs.model.TradeData tradeData = com.utsmyanmar.paylibs.utils.params.Params.newTrade(false);
com.utsmyanmar.paylibs.model.PayDetail payDetail = tradeData.getPayDetail();
payDetail.setSettleDataObj(settleData);
payDetail.setTransactionType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT.value);
payDetail.setTransType(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT.name);
payDetail.setAmount(totalAmount);
payDetail.setTradeAnswerCode("000");
payDetail.setBatchNo(SystemParamsOperation.getInstance().getCurrentBatchNum());
// Insert QR settlement record
repository.insertPayDetail(payDetail);
// Delete settled QR transactions
for (com.utsmyanmar.paylibs.model.PayDetail pay : qrTransactionsList) {
repository.deletePayDetail(pay);
}
// Delete all other QR transactions (failed ones)
for (com.utsmyanmar.paylibs.model.PayDetail pay : qrTransListAll) {
repository.deletePayDetail(pay);
}
// Push e-receipt data
try {
com.utsmyanmar.baselib.network.model.e_receipt.EReceiptRequest request =
com.utsmm.kbz.util.EReceiptUtil.getInstance().generateQRSettlement(payDetail);
LogUtil.d(TAG, "QR Settlement e-receipt data prepared");
} catch (Exception e) {
LogUtil.e(TAG, "Error preparing QR settlement e-receipt: " + e.getMessage());
}
try {
// Start MainActivity with QR settlement data for automatic printing
Intent uiIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class);
uiIntent.putExtra("AUTO_QR_SETTLEMENT", true);
uiIntent.putExtra("EXTRA_TRANSACTION_TYPE", com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT.value);
uiIntent.putExtra("QR_SALE_COUNT", qrSaleCount);
uiIntent.putExtra("QR_SALE_AMOUNT", qrSaleAmount);
uiIntent.putExtra("QR_REFUND_COUNT", qrRefundCount);
uiIntent.putExtra("QR_REFUND_AMOUNT", qrRefundAmount);
uiIntent.putExtra("QR_TOTAL_AMOUNT", totalAmount);
uiIntent.putExtra("QR_TRANS_LIST", qrTransactionsList);
uiIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(uiIntent);
LogUtil.d(TAG, "MainActivity started successfully with QR auto settlement result");
} catch (Exception e) {
LogUtil.e(TAG, "Error starting MainActivity for QR settlement: " + e.getMessage());
e.printStackTrace();
}
LogUtil.d(TAG, "QR Auto Settlement completed successfully");
} else {
LogUtil.d(TAG, "No QR transactions found for auto settlement");
}
} }
});
}); checkIfBothSettlementsComplete();
}
};
repository.getTransactionHistory().observeForever(qrSettlementObserver);
}
private void checkIfBothSettlementsComplete() {
// Stop service only when both settlements are processed (or skipped if no data)
if (regularSettlementCompleted && qrSettlementCompleted) {
LogUtil.d(TAG, "Both settlements completed, stopping service");
stopSelf();
}
} }
@Override @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
handler = new Handler(); handler = new Handler();
} }
@Nullable @Nullable
@ -278,4 +425,19 @@ public class AutoSettleService extends Service {
public boolean onUnbind(Intent intent) { public boolean onUnbind(Intent intent) {
return super.onUnbind(intent); return super.onUnbind(intent);
} }
}
@Override
public void onDestroy() {
super.onDestroy();
// Clean up observers to prevent memory leaks
if (regularSettlementObserver != null && repository != null) {
repository.getSettlementPOS().removeObserver(regularSettlementObserver);
}
if (qrSettlementObserver != null && repository != null) {
repository.getTransactionHistory().removeObserver(qrSettlementObserver);
}
LogUtil.d(TAG, "AutoSettleService destroyed and cleaned up");
}
}

View File

@ -113,7 +113,8 @@ public class TransactionResultFragment extends DataBindingFragment implements Da
} }
if (resultStr.equals(Constant.ANSWER_CODE_ACCEPT) || resultStr.equals(Constant.ANSWER_CODE_APPROVED)) { if (resultStr.equals(Constant.ANSWER_CODE_ACCEPT) || resultStr.equals(Constant.ANSWER_CODE_APPROVED)) {
/* ISO 8583 RESPONSE */ /* ISO 8583 RESPONSE */
if(sharedViewModel.payDetail.getValue().getTransactionType() != TransactionsType.SETTLEMENT.value) { if(sharedViewModel.payDetail.getValue().getTransactionType() != TransactionsType.SETTLEMENT.value
&& sharedViewModel.payDetail.getValue().getTransactionType() != TransactionsType.MMQR_SETTLEMENT.value) {
if(SystemParamsOperation.getInstance().isAlertSound()) { if(SystemParamsOperation.getInstance().isAlertSound()) {
startSound(getResourceString(R.string.txt_audio_thank_you)); startSound(getResourceString(R.string.txt_audio_thank_you));

View File

@ -32,6 +32,7 @@ import com.utsmm.kbz.util.tms.TMSUtil;
import java.io.IOException; import java.io.IOException;
import java.util.Calendar; import java.util.Calendar;
import java.util.Date;
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.CompositeDisposable;
@ -219,7 +220,7 @@ public class TMSProcessFragment extends DataBindingFragment {
CurrencyType currencyType = SystemParamsOperation.getInstance().getCurrencyType(); CurrencyType currencyType = SystemParamsOperation.getInstance().getCurrencyType();
sharedViewModel.set_currencyText(currencyType.name); sharedViewModel.set_currencyText(currencyType.name);
// tmsProcessViewModel.loadEmvParameters(); // tmsProcessViewModel.loadEmvParameters();
// scheduleAutoSettlement(); scheduleAutoSettlement();
navigateToMain(); navigateToMain();
} }
}); });
@ -278,11 +279,13 @@ public class TMSProcessFragment extends DataBindingFragment {
LogUtil.d(TAG, "Current time: " + new java.util.Date(currentTime).toString()); LogUtil.d(TAG, "Current time: " + new java.util.Date(currentTime).toString());
LogUtil.d(TAG, "Initial trigger time: " + new java.util.Date(triggerAtMillis).toString()); LogUtil.d(TAG, "Initial trigger time: " + new java.util.Date(triggerAtMillis).toString());
if (currentTime > triggerAtMillis) { if (triggerAtMillis <= currentTime) {
calendar.add(Calendar.DAY_OF_YEAR, 1); calendar.add(Calendar.DAY_OF_YEAR, 1);
triggerAtMillis = calendar.getTimeInMillis(); triggerAtMillis = calendar.getTimeInMillis();
LogUtil.d(TAG, "Trigger time is in the past, scheduling for next day: " + new java.util.Date(triggerAtMillis).toString()); LogUtil.d(TAG, "Updated trigger time for NEXT DAY: " + new java.util.Date(triggerAtMillis).toString());
} else {
LogUtil.d(TAG, "Scheduling alarm for TODAY: " + new java.util.Date(triggerAtMillis).toString());
} }
Intent intent = new Intent(requireContext(), AutoAlarmReceiver.class); Intent intent = new Intent(requireContext(), AutoAlarmReceiver.class);
@ -294,50 +297,33 @@ public class TMSProcessFragment extends DataBindingFragment {
); );
try { try {
// For Android 12+ (API 31+), we need to handle exact alarms differently alarmManager.cancel(pendingIntent);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
// Check if the app can schedule exact alarms if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
if (alarmManager.canScheduleExactAlarms()) { if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, AlarmManager.INTERVAL_DAY, pendingIntent); if (alarmManager.canScheduleExactAlarms()) {
LogUtil.d(TAG, "Auto settlement alarm scheduled successfully for: " + new java.util.Date(triggerAtMillis).toString()); alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
} else {
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
}
} else { } else {
LogUtil.d(TAG, "App doesn't have permission to schedule exact alarms. Using inexact alarm."); alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
// Use inexact alarm as fallback
alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, AlarmManager.INTERVAL_DAY, pendingIntent);
LogUtil.d(TAG, "Auto settlement inexact alarm scheduled for: " + new java.util.Date(triggerAtMillis).toString());
} }
} else { } else {
// For older Android versions alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, triggerAtMillis, AlarmManager.INTERVAL_DAY, pendingIntent);
LogUtil.d(TAG, "Auto settlement alarm scheduled successfully for: " + new java.util.Date(triggerAtMillis).toString());
} }
// Also try to schedule a test alarm 30 seconds from now for debugging long hoursUntilTrigger = (triggerAtMillis - currentTime) / 1000 / 60 / 60;
long testTriggerTime = currentTime + 30000; // 30 seconds from now long minutesUntilTrigger = ((triggerAtMillis - currentTime) / 1000 / 60) % 60;
Intent testIntent = new Intent(requireContext(), AutoAlarmReceiver.class); LogUtil.d(TAG, "Auto settlement alarm setup completed. Next occurrence: " + new Date(triggerAtMillis).toString());
testIntent.putExtra("TEST_ALARM", true); LogUtil.d(TAG, "Time until next trigger: " + hoursUntilTrigger + " hours, " + minutesUntilTrigger + " minutes");
PendingIntent testPendingIntent = PendingIntent.getBroadcast(
requireContext(),
1002,
testIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
// Schedule test alarm
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
if (alarmManager.canScheduleExactAlarms()) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, testTriggerTime, testPendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, testTriggerTime, testPendingIntent);
}
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, testTriggerTime, testPendingIntent);
}
LogUtil.d(TAG, "Test alarm scheduled for: " + new java.util.Date(testTriggerTime).toString());
} catch (Exception e) { } catch (Exception e) {
LogUtil.d(TAG, "Error scheduling alarm: " + e.getMessage()); LogUtil.e(TAG, "Error scheduling main alarm: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
LogUtil.e(TAG, "Alarm scheduling failed - Check if app has SCHEDULE_EXACT_ALARM permission");
LogUtil.e(TAG, "Failed alarm details - Target time: " + new Date(triggerAtMillis).toString());
} }
} }

View File

@ -201,7 +201,7 @@ public abstract class BaseXPrint {
} }
protected void dashBreakEnding() { protected void dashBreakEnding() {
printer.appendPrnStr("--------X----------X----------", fontNormal, AlignEnum.LEFT,false); printer.appendPrnStr("--------X----------X---------", fontNormal, AlignEnum.LEFT,false);
} }