fixed ecr connection issues

This commit is contained in:
kizzy 2026-08-19 14:49:21 +07:00
parent 60fbc471af
commit acea64c89b
5 changed files with 78 additions and 32 deletions

View File

@ -156,6 +156,9 @@ public class MainActivity extends AppCompatActivity implements
// Initialize special back handling fragments // Initialize special back handling fragments
initSpecialBackHandlingFragments(); initSpecialBackHandlingFragments();
// ECR serial receive must live on Activity — MainFragment destroy clears listeners.
registerEcrSerialBridge();
final Handler handler = new Handler(Looper.getMainLooper()); final Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(new Runnable() { handler.postDelayed(new Runnable() {
@Override @Override
@ -167,6 +170,23 @@ public class MainActivity extends AppCompatActivity implements
} }
private void registerEcrSerialBridge() {
boolean ecrOn = SystemParamsOperation.getInstance().getECRStatus();
boolean cmhlOn = SystemParamsOperation.getInstance().isCMHLEnabled();
LogUtil.d(TAG, "RegisterEcrSerialBridge ecr=" + ecrOn + " cmhl=" + cmhlOn);
ECRHelper.INSTANCE.initSerialPort(getApplicationContext());
ECRHelper.INSTANCE.setOnECRReceive(bytes -> {
if (bytes == null || bytes.length == 0) {
return null;
}
LogUtil.d(TAG, "ECR bridge forwarding len=" + bytes.length);
sharedViewModel.ecrSerialData.postValue(bytes);
return null;
});
}
private void initKey(){ private void initKey(){
// SystemParamsOperation.getInstance().setTmsAddress("https://api-tms-uat.kbzbank.com:8443/sirius"); // SystemParamsOperation.getInstance().setTmsAddress("https://api-tms-uat.kbzbank.com:8443/sirius");
@ -443,6 +463,9 @@ public class MainActivity extends AppCompatActivity implements
SystemParamsOperation.getInstance().setSetupEcr(false); SystemParamsOperation.getInstance().setSetupEcr(false);
SystemParamsOperation.getInstance().setDownloadedParams(false); SystemParamsOperation.getInstance().setDownloadedParams(false);
// Re-bind in case a fragment cleared the listener.
registerEcrSerialBridge();
handleAutoSettlementIntent(getIntent()); handleAutoSettlementIntent(getIntent());
BaseApplication.getInstance().deviceEngine.getPlatform().hideNavigationBar(); BaseApplication.getInstance().deviceEngine.getPlatform().hideNavigationBar();
} }
@ -557,6 +580,7 @@ public class MainActivity extends AppCompatActivity implements
// Clean up RxJava disposables // Clean up RxJava disposables
compositeDisposable.clear(); compositeDisposable.clear();
// Reset screen mode // Reset screen mode
ECRHelper.INSTANCE.setOnECRReceive(null);
// Disconnect serial port // Disconnect serial port
ECRHelper.INSTANCE.releaseSerialPort(); ECRHelper.INSTANCE.releaseSerialPort();
BaseApplication.getInstance().deviceEngine.getPlatform().showNavigationBar(); BaseApplication.getInstance().deviceEngine.getPlatform().showNavigationBar();

View File

@ -43,7 +43,6 @@ import com.utsmyanmar.baselib.util.DataBindingConfig;
import com.utsmyanmar.baselib.util.DialogCallback; import com.utsmyanmar.baselib.util.DialogCallback;
import com.utsmyanmar.baselib.util.TerminalUtil; import com.utsmyanmar.baselib.util.TerminalUtil;
import com.utsmyanmar.checkxread.sdk.NexGoSDK; import com.utsmyanmar.checkxread.sdk.NexGoSDK;
import com.utsmyanmar.ecr.ECRHelper;
import com.utsmyanmar.ecr.ECRProcess; import com.utsmyanmar.ecr.ECRProcess;
import com.utsmyanmar.ecr.data.ResultOf; import com.utsmyanmar.ecr.data.ResultOf;
import com.utsmyanmar.ecr.data.model.Transactions; import com.utsmyanmar.ecr.data.model.Transactions;
@ -139,24 +138,16 @@ public class MainFragment extends DataBindingFragment {
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
if (SystemParamsOperation.getInstance().getECRStatus()) {
ECRHelper.INSTANCE.setOnECRReceive(bytes -> {
if (getActivity() == null) {
return null;
}
requireActivity().runOnUiThread(() -> handleSerialData(bytes));
return null;
});
ECRHelper.INSTANCE.initSerialPort(requireContext().getApplicationContext());
LogUtil.d(TAG, "ECR SerialPortDriver listening");
}
} }
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
ECRHelper.INSTANCE.setOnECRReceive(bytes -> null); }
@Override
public void onDestroy() {
super.onDestroy();
} }
private void handleSerialData(byte[] data) { private void handleSerialData(byte[] data) {
@ -167,8 +158,10 @@ public class MainFragment extends DataBindingFragment {
if (SystemParamsOperation.getInstance().isCMHLEnabled()) { if (SystemParamsOperation.getInstance().isCMHLEnabled()) {
ECRActionCHML(data); ECRActionCHML(data);
} else { } else if (SystemParamsOperation.getInstance().getECRStatus()) {
ECRAction(new String(data, StandardCharsets.UTF_8)); ECRAction(new String(data, StandardCharsets.UTF_8));
} else {
LogUtil.e(TAG, "ECR/CMHL disabled — ignoring serial packet");
} }
} }
@ -258,15 +251,6 @@ public class MainFragment extends DataBindingFragment {
} }
} }
@Override
public void onDestroy() {
super.onDestroy();
// BaseApplication.getInstance().deviceEngine.getPlatform().showNavigationBar();
// enableTaskButton();
// enableHomeButton();
}
@Override @Override
public void onPause() { public void onPause() {
super.onPause(); super.onPause();
@ -288,6 +272,14 @@ public class MainFragment extends DataBindingFragment {
} }
sharedViewModel.setManualEntryStatus(SystemParamsOperation.getInstance().getManualEntryStatus()); sharedViewModel.setManualEntryStatus(SystemParamsOperation.getInstance().getManualEntryStatus());
sharedViewModel.ecrSerialData.observe(getViewLifecycleOwner(), bytes -> {
if (bytes == null || bytes.length == 0) {
return;
}
LogUtil.d(TAG, "ECR LiveData received len=" + bytes.length);
handleSerialData(bytes);
});
// queryAidCAPKList(); // queryAidCAPKList();
queryAidCAPKList(); queryAidCAPKList();
// generateMockQR(); // generateMockQR();
@ -786,9 +778,10 @@ public class MainFragment extends DataBindingFragment {
private void ECRActionCHML(byte[] data) { private void ECRActionCHML(byte[] data) {
// LogUtil.d(TAG, "ECR msg here : " + msg); LogUtil.d(TAG, "ECR CMHL packet len=" + data.length);
try { try {
EcrPacket ecrPacket = EcrManager.parsePacket(data); EcrPacket ecrPacket = EcrManager.parsePacket(data);
LogUtil.d(TAG, "ECR CMHL cmd=0x" + Integer.toHexString(ecrPacket.getCommandCode() & 0xFF));
EcrRequestDispatcher.dispatch(ecrPacket, new EcrRequestListener() { EcrRequestDispatcher.dispatch(ecrPacket, new EcrRequestListener() {
@Override @Override
@ -809,6 +802,8 @@ public class MainFragment extends DataBindingFragment {
if (CoreUtils.getInstance(sharedViewModel).setUpECRSale(request)) { if (CoreUtils.getInstance(sharedViewModel).setUpECRSale(request)) {
LogUtil.d(TAG,"Starting to process sale transaction!"); LogUtil.d(TAG,"Starting to process sale transaction!");
delayFunctionCall(() -> isRouteValidAndNavigateToRoute(R.id.action_nav_main_to_cardWaitingFragment)); delayFunctionCall(() -> isRouteValidAndNavigateToRoute(R.id.action_nav_main_to_cardWaitingFragment));
} else {
LogUtil.e(TAG, "CMHL Sale setup failed");
} }

View File

@ -87,6 +87,8 @@ public class SharedViewModel extends ViewModel {
/*May 31 2022*/ /*May 31 2022*/
public SingleLiveEvent<Boolean> isEcrFinished = new SingleLiveEvent<>(); public SingleLiveEvent<Boolean> isEcrFinished = new SingleLiveEvent<>();
public final SingleLiveEvent<byte[]> ecrSerialData = new SingleLiveEvent<>();
/*Jun 3 2022*/ /*Jun 3 2022*/
public SingleLiveEvent<Boolean> isEmv = new SingleLiveEvent<>(); public SingleLiveEvent<Boolean> isEmv = new SingleLiveEvent<>();

View File

@ -250,7 +250,7 @@ public class CoreUtils implements OldECRSetups,ECRSetups,ECRSetupsCMHL {
try{ try{
LogUtil.d(TAG, "amount is "+trans.getAmount()); LogUtil.d(TAG, "amount is "+trans.getAmount());
if (trans.getAmount() == null && Long.parseLong(trans.getAmount()) <= 0) { if (trans.getAmount() == null || Long.parseLong(trans.getAmount()) <= 0) {
LogUtil.d(TAG,"Amount field is invalid!"); LogUtil.d(TAG,"Amount field is invalid!");
return false; return false;
@ -276,7 +276,7 @@ public class CoreUtils implements OldECRSetups,ECRSetups,ECRSetupsCMHL {
try{ try{
LogUtil.d(TAG, "amount is "+trans.getAmount()); LogUtil.d(TAG, "amount is "+trans.getAmount());
if (trans.getAmount() == null && Long.parseLong(trans.getAmount()) <= 0) { if (trans.getAmount() == null || Long.parseLong(trans.getAmount()) <= 0) {
LogUtil.d(TAG,"Amount field is invalid!"); LogUtil.d(TAG,"Amount field is invalid!");
return false; return false;
@ -324,7 +324,7 @@ public class CoreUtils implements OldECRSetups,ECRSetups,ECRSetupsCMHL {
try{ try{
LogUtil.d(TAG, "amount is "+trans.getAmount()); LogUtil.d(TAG, "amount is "+trans.getAmount());
if (trans.getAmount() == null && Long.parseLong(trans.getAmount()) <= 0) { if (trans.getAmount() == null || Long.parseLong(trans.getAmount()) <= 0) {
LogUtil.d(TAG,"Amount field is invalid!"); LogUtil.d(TAG,"Amount field is invalid!");
return false; return false;

View File

@ -1,6 +1,8 @@
package com.utsmyanmar.ecr package com.utsmyanmar.ecr
import android.content.Context import android.content.Context
import android.os.Handler
import android.os.Looper
import com.nexgo.oaf.apiv3.APIProxy import com.nexgo.oaf.apiv3.APIProxy
import com.nexgo.oaf.apiv3.SdkResult import com.nexgo.oaf.apiv3.SdkResult
import com.nexgo.oaf.apiv3.device.serialport.SerialCfgEntity import com.nexgo.oaf.apiv3.device.serialport.SerialCfgEntity
@ -8,6 +10,7 @@ import com.nexgo.oaf.apiv3.device.serialport.SerialPortDriver
import com.utsmyanmar.paylibs.utils.LogUtil import com.utsmyanmar.paylibs.utils.LogUtil
import com.utsmyanmar.paylibs.utils.core_utils.ByteUtil import com.utsmyanmar.paylibs.utils.core_utils.ByteUtil
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
/** /**
* NexGo SerialPortDriver wrapper (port 0 = physical USB serial). * NexGo SerialPortDriver wrapper (port 0 = physical USB serial).
@ -17,13 +20,15 @@ object ECRHelper {
val TAG: String = ECRHelper::class.java.simpleName val TAG: String = ECRHelper::class.java.simpleName
private const val PORT_NO = 101 private const val PORT_NO = 0
private const val RECV_BUFFER_SIZE = 2048 private const val RECV_BUFFER_SIZE = 2048
private const val RECV_TIMEOUT_MS = 3000L private const val RECV_TIMEOUT_MS = 3000L
private var serialPort: SerialPortDriver? = null private var serialPort: SerialPortDriver? = null
private var receiveThread: Thread? = null private var receiveThread: Thread? = null
private val receiving = AtomicBoolean(false) private val receiving = AtomicBoolean(false)
private val mainHandler = Handler(Looper.getMainLooper())
private val receiveListener = AtomicReference<((ByteArray) -> Unit)?>(null)
var onBindSuccess: () -> Unit = { } var onBindSuccess: () -> Unit = { }
var onBindFailure: () -> Unit = { } var onBindFailure: () -> Unit = { }
@ -34,7 +39,11 @@ object ECRHelper {
var onSendSuccess: () -> Unit = { } var onSendSuccess: () -> Unit = { }
var onSendFailure: (Int, String) -> Unit = { _, _ -> } var onSendFailure: (Int, String) -> Unit = { _, _ -> }
var onECRReceive: (bytes: ByteArray) -> Unit = { } /** Thread-safe receive callback. Invoked on the main thread. */
fun setOnECRReceive(listener: ((ByteArray) -> Unit)?) {
receiveListener.set(listener)
LogUtil.d(TAG, "onECRReceive listener ${if (listener != null) "registered" else "cleared"}")
}
val isConnected: Boolean val isConnected: Boolean
get() = serialPort != null get() = serialPort != null
@ -139,6 +148,22 @@ object ECRHelper {
} }
} }
private fun dispatchReceive(data: ByteArray) {
val listener = receiveListener.get()
if (listener == null) {
LogUtil.e(TAG, "Serial data dropped: no onECRReceive listener")
return
}
mainHandler.post {
try {
listener.invoke(data)
} catch (ex: Throwable) {
LogUtil.e(TAG, "onECRReceive error: ${ex.message}")
ex.printStackTrace()
}
}
}
private fun startReceiveLoop() { private fun startReceiveLoop() {
if (receiving.getAndSet(true)) { if (receiving.getAndSet(true)) {
return return
@ -153,8 +178,8 @@ object ECRHelper {
if (len > 0) { if (len > 0) {
val data = buffer.copyOf(len) val data = buffer.copyOf(len)
LogUtil.d(TAG, "Serial recv len=$len") LogUtil.d(TAG, "Serial recv len=$len")
LogUtil.d(TAG," Receive packet : "+ ByteUtil.bytes2HexStr(data)); LogUtil.d(TAG, " Receive packet : " + ByteUtil.bytes2HexStr(data))
onECRReceive(data) dispatchReceive(data)
} }
// timeout / no data: keep listening // timeout / no data: keep listening
} catch (ex: Throwable) { } catch (ex: Throwable) {