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,9 +416,10 @@ public class MainActivity extends AppCompatActivity implements
private void handleAutoSettlementIntent(Intent intent) { private void handleAutoSettlementIntent(Intent intent) {
if (intent == null) return; if (intent == null) return;
boolean auto = intent.getBooleanExtra("AUTO_SETTLEMENT", false);
if (!auto) return;
// Handle regular auto settlement
boolean auto = intent.getBooleanExtra("AUTO_SETTLEMENT", false);
if (auto) {
com.utsmyanmar.paylibs.model.PayDetail payDetail = (com.utsmyanmar.paylibs.model.PayDetail) intent.getSerializableExtra("EXTRA_PAY_DETAIL"); com.utsmyanmar.paylibs.model.PayDetail payDetail = (com.utsmyanmar.paylibs.model.PayDetail) intent.getSerializableExtra("EXTRA_PAY_DETAIL");
if (payDetail != null) { if (payDetail != null) {
sharedViewModel.payDetail.setValue(payDetail); sharedViewModel.payDetail.setValue(payDetail);
@ -427,6 +430,43 @@ public class MainActivity extends AppCompatActivity implements
LogUtil.e(TAG, "Navigation error: " + e.getMessage()); 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");
// Create QR settlement PayDetail
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.transactionsType.setValue(com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_SETTLEMENT);
try {
navController.navigate(R.id.transactionResultFragment);
} catch (Exception e) {
LogUtil.e(TAG, "QR Auto Settlement navigation error: " + e.getMessage());
}
}
} }
@Override @Override

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!");
}
// Start the service to send the API request
Intent serviceIntent = new Intent(context, AutoSettleService.class); Intent serviceIntent = new Intent(context, AutoSettleService.class);
context.startService(serviceIntent); context.startService(serviceIntent);
LogUtil.d(TAG, "AutoSettleService started successfully");
} catch (Exception e) {
LogUtil.e(TAG, "Failed to start AutoSettleService: " + e.getMessage());
e.printStackTrace();
}
}
LogUtil.d(TAG, "AutoSettleService started"); private void scheduleNextDayAlarm(Context context) {
try {
String timeStr = SystemParamsOperation.getInstance().getClearBatchTime();
LogUtil.d(TAG, " Rescheduling next day's alarm for timeStr: " + timeStr);
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,13 +104,27 @@ 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>>() {
@Override
public void onChanged(java.util.List<com.utsmyanmar.paylibs.model.PayDetail> list) {
if (regularSettlementCompleted) {
return;
}
regularSettlementCompleted = true;
// Remove observer immediately to prevent infinite loop
repository.getSettlementPOS().removeObserver(this);
int saleCount = 0; int saleCount = 0;
long saleAmount = 0L; long saleAmount = 0L;
int preCount = 0; int preCount = 0;
@ -141,15 +162,7 @@ public class AutoSettleService extends Service {
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,
preCount,
preAmount,
refundCount,
refundAmount,
caCount,
caAmount
);
payDetail.setSettleDataObj(settleData); payDetail.setSettleDataObj(settleData);
long totalAmount = saleAmount + preAmount + refundAmount + caAmount; long totalAmount = saleAmount + preAmount + refundAmount + caAmount;
@ -212,8 +225,6 @@ public class AutoSettleService extends Service {
repository.insertPayDetail(payDetail); repository.insertPayDetail(payDetail);
try { try {
// Remove the non-serializable SettleData object before passing through Intent
// Create a clean copy of PayDetail without the problematic SettleData object
com.utsmyanmar.paylibs.model.PayDetail cleanPayDetail = new com.utsmyanmar.paylibs.model.PayDetail(); com.utsmyanmar.paylibs.model.PayDetail cleanPayDetail = new com.utsmyanmar.paylibs.model.PayDetail();
cleanPayDetail.setTransType(payDetail.getTransType()); cleanPayDetail.setTransType(payDetail.getTransType());
cleanPayDetail.setTransactionType(payDetail.getTransactionType()); cleanPayDetail.setTransactionType(payDetail.getTransactionType());
@ -223,7 +234,6 @@ public class AutoSettleService extends Service {
cleanPayDetail.setAmount(payDetail.getAmount()); cleanPayDetail.setAmount(payDetail.getAmount());
cleanPayDetail.setTradeAnswerCode(payDetail.getTradeAnswerCode()); cleanPayDetail.setTradeAnswerCode(payDetail.getTradeAnswerCode());
cleanPayDetail.setIsNeedReversal(payDetail.getIsNeedReversal()); cleanPayDetail.setIsNeedReversal(payDetail.getIsNeedReversal());
// Note: Not setting SettleDataObj as it's not serializable
Intent uiIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class); Intent uiIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class);
uiIntent.putExtra("AUTO_SETTLEMENT", true); uiIntent.putExtra("AUTO_SETTLEMENT", true);
@ -238,7 +248,6 @@ public class AutoSettleService extends Service {
LogUtil.e(TAG, "Error starting MainActivity: " + e.getMessage()); LogUtil.e(TAG, "Error starting MainActivity: " + e.getMessage());
e.printStackTrace(); e.printStackTrace();
// Fallback: start MainActivity without the PayDetail extra
try { try {
Intent fallbackIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class); Intent fallbackIntent = new Intent(getApplicationContext(), com.utsmm.kbz.MainActivity.class);
fallbackIntent.putExtra("AUTO_SETTLEMENT", true); fallbackIntent.putExtra("AUTO_SETTLEMENT", true);
@ -253,19 +262,157 @@ public class AutoSettleService extends Service {
} }
} }
// Stop the service checkIfBothSettlementsComplete();
stopSelf();
} }
}); });
}); }
};
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;
// Remove observer immediately to prevent infinite loop
repository.getTransactionHistory().removeObserver(this);
if (payDetailList != null) {
int qrSaleCount = 0;
long qrSaleAmount = 0;
int qrRefundCount = 0;
long qrRefundAmount = 0;
java.util.ArrayList<com.utsmyanmar.paylibs.model.PayDetail> qrTransactionsList = new java.util.ArrayList<>();
java.util.ArrayList<com.utsmyanmar.paylibs.model.PayDetail> qrTransListAll = new java.util.ArrayList<>();
try {
for (com.utsmyanmar.paylibs.model.PayDetail payDetail : payDetailList) {
// Filter for successful QR transactions only
if ((payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR.value
|| payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_REFUND.value)
&& payDetail.getQrTransStatus() == 1) {
qrTransactionsList.add(payDetail);
if (payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR.value) {
qrSaleCount++;
qrSaleAmount += payDetail.getAmount();
} else if (payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_REFUND.value) {
qrRefundCount++;
qrRefundAmount += payDetail.getAmount();
}
} else if (payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR.value
|| payDetail.getTransactionType() == com.utsmyanmar.paylibs.utils.iso_utils.TransactionsType.MMQR_REFUND.value) {
// Include all QR transactions for deletion (successful and failed)
qrTransListAll.add(payDetail);
}
}
} catch (IllegalStateException e) {
LogUtil.e(TAG, "QR Auto Settlement: Database cursor error - likely due to large data size: " + e);
checkIfBothSettlementsComplete();
return;
}
if (!qrTransactionsList.isEmpty()) {
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();
} }
}); });
@ -279,10 +280,12 @@ 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 (alarmManager.canScheduleExactAlarms()) {
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());
} else {
LogUtil.d(TAG, "App doesn't have permission to schedule exact alarms. Using inexact alarm.");
// 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 {
// For older Android versions
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 if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
long testTriggerTime = currentTime + 30000; // 30 seconds from now
Intent testIntent = new Intent(requireContext(), AutoAlarmReceiver.class);
testIntent.putExtra("TEST_ALARM", true);
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 (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
if (alarmManager.canScheduleExactAlarms()) { if (alarmManager.canScheduleExactAlarms()) {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, testTriggerTime, testPendingIntent); alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
} else { } else {
alarmManager.set(AlarmManager.RTC_WAKEUP, testTriggerTime, testPendingIntent); alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
} }
} else { } else {
alarmManager.set(AlarmManager.RTC_WAKEUP, testTriggerTime, testPendingIntent); alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
} }
LogUtil.d(TAG, "Test alarm scheduled for: " + new java.util.Date(testTriggerTime).toString()); } else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
}
long hoursUntilTrigger = (triggerAtMillis - currentTime) / 1000 / 60 / 60;
long minutesUntilTrigger = ((triggerAtMillis - currentTime) / 1000 / 60) % 60;
LogUtil.d(TAG, "Auto settlement alarm setup completed. Next occurrence: " + new Date(triggerAtMillis).toString());
LogUtil.d(TAG, "Time until next trigger: " + hoursUntilTrigger + " hours, " + minutesUntilTrigger + " minutes");
} 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);
} }