diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 2e7dcfd..f5ad28e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -73,12 +73,8 @@
android:exported="false" />
-
-
+ android:name=".transaction.CreateTransactionActivity"
+ android:exported="false" />
{
- Button btn = (Button) v;
- String number = btn.getText().toString();
- appendToAmount(number);
- };
-
- btn1.setOnClickListener(numberClickListener);
- btn2.setOnClickListener(numberClickListener);
- btn3.setOnClickListener(numberClickListener);
- btn4.setOnClickListener(numberClickListener);
- btn5.setOnClickListener(numberClickListener);
- btn6.setOnClickListener(numberClickListener);
- btn7.setOnClickListener(numberClickListener);
- btn8.setOnClickListener(numberClickListener);
- btn9.setOnClickListener(numberClickListener);
- btn0.setOnClickListener(numberClickListener);
- btn00.setOnClickListener(numberClickListener);
-
- // Clear button
- btnClear.setOnClickListener(v -> clearAmount());
-
- // Confirm button
- btnConfirm.setOnClickListener(v -> handleConfirmAmount());
-
- // Toggle mode button
- btnToggleMode.setOnClickListener(v -> toggleEMVMode());
- }
-
- private void appendToAmount(String number) {
- // Remove leading zeros and handle maximum amount
- String currentAmount = transactionAmount.equals("0") ? "" : transactionAmount;
- String newAmount = currentAmount + number;
-
- // Limit to reasonable amount (max 999999999 = 9,999,999.99)
- if (newAmount.length() <= 9) {
- transactionAmount = newAmount;
- updateAmountDisplay();
- }
- }
-
- private void clearAmount() {
- if (transactionAmount.length() > 1) {
- transactionAmount = transactionAmount.substring(0, transactionAmount.length() - 1);
- } else {
- transactionAmount = "0";
- }
- updateAmountDisplay();
- }
-
- private void updateAmountDisplay() {
- if (tvAmountDisplay != null) {
- long amountCents = Long.parseLong(transactionAmount);
- double amountRupiah = amountCents / 100.0;
- NumberFormat formatter = NumberFormat.getCurrencyInstance(new Locale("id", "ID"));
- String formattedAmount = formatter.format(amountRupiah);
- tvAmountDisplay.setText(formattedAmount);
- }
- }
-
- private void handleConfirmAmount() {
- long amountCents = Long.parseLong(transactionAmount);
- if (amountCents < 100) { // Minimum Rp 1.00
- showToast("Minimum amount is Rp 1.00");
- return;
- }
-
- // Start EMV Transaction Activity
- Intent intent = new Intent(this, EmvTransactionActivity.class);
- intent.putExtra("TRANSACTION_AMOUNT", transactionAmount);
- intent.putExtra("EMV_MODE", isEMVMode);
- startActivity(intent);
- finish(); // Remove this activity from stack
- }
-
- private void toggleEMVMode() {
- isEMVMode = !isEMVMode;
- updateModeDisplay();
- showToast("Mode switched to: " + (isEMVMode ? "EMV Mode" : "Simple Mode"));
- }
-
- private void updateModeDisplay() {
- String mode = isEMVMode ? "EMV Mode (Full Card Data)" : "Simple Mode (Basic Detection)";
- if (tvModeIndicator != null) {
- tvModeIndicator.setText("Current Mode: " + mode);
- }
- if (btnToggleMode != null) {
- btnToggleMode.setText(isEMVMode ? "Switch to Simple" : "Switch to EMV");
- }
- }
-
- private void showToast(String message) {
- Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
- }
-
- @Override
- public boolean onSupportNavigateUp() {
- onBackPressed();
- return true;
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bdkipoc/kredit/CreditCardActivity.java b/app/src/main/java/com/example/bdkipoc/kredit/CreditCardActivity.java
index 2318b79..960a264 100644
--- a/app/src/main/java/com/example/bdkipoc/kredit/CreditCardActivity.java
+++ b/app/src/main/java/com/example/bdkipoc/kredit/CreditCardActivity.java
@@ -32,6 +32,8 @@ import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import com.example.bdkipoc.transaction.CreateTransactionActivity;
+
/**
* CreditCardActivity - Display detailed transaction results and TLV data
*/
diff --git a/app/src/main/java/com/example/bdkipoc/kredit/EmvTransactionActivity.java b/app/src/main/java/com/example/bdkipoc/kredit/EmvTransactionActivity.java
deleted file mode 100644
index 7922332..0000000
--- a/app/src/main/java/com/example/bdkipoc/kredit/EmvTransactionActivity.java
+++ /dev/null
@@ -1,1307 +0,0 @@
-package com.example.bdkipoc.kredit;
-
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.Looper;
-import android.os.Message;
-import android.os.RemoteException;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.view.animation.Animation;
-import android.view.animation.AnimationUtils;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
-import android.widget.ProgressBar;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import androidx.appcompat.app.AlertDialog;
-import androidx.appcompat.app.AppCompatActivity;
-import androidx.appcompat.widget.Toolbar;
-
-import com.example.bdkipoc.MyApplication;
-import com.example.bdkipoc.R;
-import com.example.bdkipoc.utils.ByteUtil;
-import com.example.bdkipoc.utils.Utility;
-import com.sunmi.pay.hardware.aidl.AidlConstants.CardType;
-import com.sunmi.pay.hardware.aidl.bean.CardInfo;
-import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2;
-import com.sunmi.pay.hardware.aidlv2.AidlErrorCodeV2;
-import com.sunmi.pay.hardware.aidlv2.bean.EMVCandidateV2;
-import com.sunmi.pay.hardware.aidlv2.bean.PinPadConfigV2;
-import com.sunmi.pay.hardware.aidlv2.emv.EMVListenerV2;
-import com.sunmi.pay.hardware.aidlv2.emv.EMVOptV2;
-import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadListenerV2;
-import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadOptV2;
-import com.sunmi.pay.hardware.aidlv2.readcard.CheckCardCallbackV2;
-
-import java.text.NumberFormat;
-import java.util.List;
-import java.util.Locale;
-import java.util.Objects;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * EmvTransactionActivity - Handle card reading and EMV processing with Modal
- */
-public class EmvTransactionActivity extends AppCompatActivity {
- private static final String TAG = "EmvTransaction";
-
- // UI Components - SIMPLIFIED + MODAL
- private LinearLayout mainContent;
- private FrameLayout modalOverlay;
- private TextView tvStatus;
- private TextView modalText;
- private ImageView modalIcon;
- private ProgressBar progressBar;
- private ImageView ivCardReader;
-
- // Animation
- private Animation fadeIn;
- private Animation fadeOut;
-
- // Transaction Data
- private String transactionAmount;
- private boolean isEMVMode;
- private boolean isProcessing = false;
- private boolean isModalShowing = false;
-
- // EMV Components
- private EMVOptV2 mEMVOptV2;
- private PinPadOptV2 mPinPadOptV2;
-
- // EMV Process Variables
- private int mCardType;
- private String mCardNo;
- private int mPinType;
- private String mCertInfo;
- private int mSelectIndex;
- private int mProcessStep;
- private AlertDialog mAppSelectDialog;
-
- // EMV Process Constants
- private static final int EMV_APP_SELECT = 1;
- private static final int EMV_FINAL_APP_SELECT = 2;
- private static final int EMV_CONFIRM_CARD_NO = 3;
- private static final int EMV_CERT_VERIFY = 4;
- private static final int EMV_SHOW_PIN_PAD = 5;
- private static final int EMV_ONLINE_PROCESS = 6;
- private static final int EMV_SIGNATURE = 7;
- private static final int EMV_TRANS_SUCCESS = 888;
- private static final int EMV_TRANS_FAIL = 999;
-
- private static final int PIN_CLICK_PIN = 51;
- private static final int PIN_CLICK_CONFIRM = 52;
- private static final int PIN_CLICK_CANCEL = 53;
- private static final int PIN_ERROR = 54;
-
- // Modal Display Messages
- private static final int SHOW_MODAL_SCAN_CARD = 101;
- private static final int SHOW_MODAL_PROCESSING = 102;
- private static final int HIDE_MODAL = 103;
-
- private final Handler mHandler = new Handler(Looper.getMainLooper()) {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case EMV_FINAL_APP_SELECT:
- importFinalAppSelectStatus(0);
- break;
-
- case EMV_APP_SELECT:
- String[] candiNames = (String[]) msg.obj;
- showAppSelectDialog(candiNames);
- break;
-
- case EMV_CONFIRM_CARD_NO:
- showCardNumberConfirmation();
- break;
-
- case EMV_CERT_VERIFY:
- showCertificateVerification();
- break;
-
- case EMV_SHOW_PIN_PAD:
- android.util.Log.d(TAG, "Initializing PIN pad...");
- hideModal();
- initPinPad();
- break;
-
- case EMV_ONLINE_PROCESS:
- showModalProcessing("Memproses Otorisasi Online...");
- mockOnlineProcess();
- break;
-
- case EMV_SIGNATURE:
- importSignatureStatus(0);
- break;
-
- case PIN_CLICK_PIN:
- android.util.Log.d(TAG, "PIN input confirmed");
- importPinInputStatus(0);
- break;
-
- case PIN_CLICK_CONFIRM:
- android.util.Log.d(TAG, "PIN confirm without input (bypass)");
- importPinInputStatus(2);
- break;
-
- case PIN_CLICK_CANCEL:
- showToast("PIN input cancelled by user");
- importPinInputStatus(1);
- break;
-
- case PIN_ERROR:
- android.util.Log.e(TAG, "PIN Error: " + msg.obj + " -- " + msg.arg1);
- showToast("PIN Error: " + msg.obj);
- importPinInputStatus(3);
- break;
-
- case EMV_TRANS_FAIL:
- // Handle EMV transaction failure with error code check
- int errorCode = msg.arg1;
- String errorDesc = (String) msg.obj;
-
- Log.e(TAG, "EMV Transaction failed: " + errorDesc + " (Code: " + errorCode + ")");
-
- hideModal();
- if (errorCode == -50009) {
- // EMV process conflict - reset and retry
- Log.d(TAG, "EMV process conflict detected - resetting...");
- showToast("EMV busy, resetting...");
- resetEMVAndRetry();
- } else {
- // Other errors - show message but don't finish
- showToast("Transaction failed: " + errorDesc);
- resetScanningState();
- }
- break;
-
- case EMV_TRANS_SUCCESS:
- // Navigate to results screen
- hideModal();
- navigateToResults();
- break;
-
- case SHOW_MODAL_SCAN_CARD:
- showModalScanCard();
- break;
-
- case SHOW_MODAL_PROCESSING:
- String processingMsg = (String) msg.obj;
- showModalProcessing(processingMsg);
- break;
-
- case HIDE_MODAL:
- hideModal();
- break;
- }
- }
- };
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_emv_transaction);
-
- getIntentData();
- initViews();
- initAnimations();
- initEMVComponents();
- initEMVData();
-
- // Auto-start scanning after short delay
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- autoStartScanning();
- }, 1000); // 1 second delay to let everything initialize
- }
-
- private void getIntentData() {
- Intent intent = getIntent();
- transactionAmount = intent.getStringExtra("TRANSACTION_AMOUNT");
- isEMVMode = intent.getBooleanExtra("EMV_MODE", true);
-
- if (transactionAmount == null) {
- transactionAmount = "0";
- finish();
- return;
- }
- }
-
- // ====== UPDATED INIT VIEWS METHOD ======
- private void initViews() {
- // Setup Toolbar with back navigation
- Toolbar toolbar = findViewById(R.id.toolbar);
- setSupportActionBar(toolbar);
- if (getSupportActionBar() != null) {
- getSupportActionBar().setDisplayHomeAsUpEnabled(true);
- getSupportActionBar().setTitle("Scan Kartu");
- }
-
- // Initialize UI components
- mainContent = findViewById(R.id.main_content);
- modalOverlay = findViewById(R.id.modal_overlay);
- tvStatus = findViewById(R.id.tv_status);
- progressBar = findViewById(R.id.progress_bar);
- ivCardReader = findViewById(R.id.iv_card_reader);
- modalText = findViewById(R.id.modal_text);
- modalIcon = findViewById(R.id.modal_icon);
-
- // Set initial state
- updateStatusUI("Initializing scanner...", true);
-
- // Setup modal overlay click to close
- modalOverlay.setOnClickListener(v -> {
- // Optional: close modal when clicking outside
- // hideModal();
- });
- }
-
- // ====== ANIMATION INITIALIZATION ======
- private void initAnimations() {
- // Create fade animations programmatically since we want to avoid new resources
- fadeIn = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
- fadeOut = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
-
- // Set animation duration
- fadeIn.setDuration(300);
- fadeOut.setDuration(300);
- }
-
- // ====== MODAL METHODS ======
- private void showModalScanCard() {
- if (isModalShowing) return;
-
- runOnUiThread(() -> {
- modalText.setText("Silakan Tempelkan / Gesekkan / Masukkan Kartu ke Perangkat");
- modalIcon.setImageResource(R.drawable.ic_card_insert);
-
- modalOverlay.setVisibility(View.VISIBLE);
- modalOverlay.startAnimation(fadeIn);
-
- isModalShowing = true;
-
- Log.d(TAG, "Modal scan card shown");
- });
- }
-
- private void showModalProcessing(String message) {
- if (!isModalShowing) {
- runOnUiThread(() -> {
- modalText.setText(message);
- modalIcon.setImageResource(R.drawable.ic_card_insert);
-
- modalOverlay.setVisibility(View.VISIBLE);
- modalOverlay.startAnimation(fadeIn);
-
- isModalShowing = true;
-
- Log.d(TAG, "Modal processing shown: " + message);
- });
- } else {
- // Just update text if modal already showing
- runOnUiThread(() -> {
- modalText.setText(message);
- Log.d(TAG, "Modal text updated: " + message);
- });
- }
- }
-
- private void hideModal() {
- if (!isModalShowing) return;
-
- runOnUiThread(() -> {
- fadeOut.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {}
-
- @Override
- public void onAnimationEnd(Animation animation) {
- modalOverlay.setVisibility(View.GONE);
- isModalShowing = false;
- }
-
- @Override
- public void onAnimationRepeat(Animation animation) {}
- });
-
- modalOverlay.startAnimation(fadeOut);
-
- Log.d(TAG, "Modal hidden");
- });
- }
-
- // ====== NEW METHOD TO UPDATE STATUS UI ======
- private void updateStatusUI(String statusText, boolean showProgress) {
- runOnUiThread(() -> {
- tvStatus.setText(statusText);
- progressBar.setVisibility(showProgress ? View.VISIBLE : View.GONE);
- });
- }
-
- private void initEMVComponents() {
- if (MyApplication.app != null) {
- mEMVOptV2 = MyApplication.app.emvOptV2;
- mPinPadOptV2 = MyApplication.app.pinPadOptV2;
- android.util.Log.d(TAG, "EMV components initialized");
- } else {
- android.util.Log.e(TAG, "MyApplication.app is null");
- showToast("Application error");
- finish();
- }
- }
-
- private void initEMVData() {
- try {
- if (mEMVOptV2 != null) {
- // Reset any existing EMV process first
- mEMVOptV2.initEmvProcess();
-
- // Small delay to ensure reset is complete
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- try {
- initEmvTlvData();
- android.util.Log.d(TAG, "EMV data initialized successfully");
- } catch (Exception e) {
- android.util.Log.e(TAG, "Error in delayed EMV init: " + e.getMessage());
- }
- }, 500);
- }
- } catch (Exception e) {
- android.util.Log.e(TAG, "Error initializing EMV data: " + e.getMessage());
- e.printStackTrace();
- }
- }
-
- private void initEmvTlvData() {
- try {
- // Set PayPass (MasterCard) TLV data
- String[] tagsPayPass = {"DF8117", "DF8118", "DF8119", "DF811F", "DF811E", "DF812C",
- "DF8123", "DF8124", "DF8125", "DF8126", "DF811B", "DF811D", "DF8122", "DF8120", "DF8121"};
- String[] valuesPayPass = {"E0", "F8", "F8", "E8", "00", "00",
- "000000000000", "000000100000", "999999999999", "000000100000",
- "30", "02", "0000000000", "000000000000", "000000000000"};
- mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYPASS, tagsPayPass, valuesPayPass);
-
- // Set AMEX TLV data
- String[] tagsAE = {"9F6D", "9F6E", "9F33", "9F35", "DF8168", "DF8167", "DF8169", "DF8170"};
- String[] valuesAE = {"C0", "D8E00000", "E0E888", "22", "00", "00", "00", "60"};
- mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_AE, tagsAE, valuesAE);
-
- // Set JCB TLV data
- String[] tagsJCB = {"9F53", "DF8161"};
- String[] valuesJCB = {"708000", "7F00"};
- mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_JCB, tagsJCB, valuesJCB);
-
- // Set DPAS TLV data
- String[] tagsDPAS = {"9F66"};
- String[] valuesDPAS = {"B600C000"};
- mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_DPAS, tagsDPAS, valuesDPAS);
-
- // Set Flash TLV data
- String[] tagsFLASH = {"9F58", "9F59", "9F5A", "9F5D", "9F5E"};
- String[] valuesFLASH = {"03", "D88700", "00", "000000000000", "E000"};
- mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_FLASH, tagsFLASH, valuesFLASH);
-
- // Set Pure TLV data
- String[] tagsPURE = {"DF7F", "DF8134", "DF8133"};
- String[] valuesPURE = {"A0000007271010", "DF", "36006043F9"};
- mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PURE, tagsPURE, valuesPURE);
-
- Bundle bundle = new Bundle();
- bundle.putBoolean("optOnlineRes", true);
- mEMVOptV2.setTermParamEx(bundle);
-
- } catch (RemoteException e) {
- android.util.Log.e(TAG, "Error setting EMV TLV data: " + e.getMessage());
- e.printStackTrace();
- }
- }
-
- // ====== UPDATED AUTO-START SCANNING METHOD ======
- private void autoStartScanning() {
- Log.d(TAG, "Auto-starting card scanning...");
-
- if (isProcessing) {
- Log.d(TAG, "Already processing - ignoring auto-start");
- return;
- }
-
- updateStatusUI("Ready for card...\n\nPlease insert, swipe, or tap your card\n\nScanning will start automatically", true);
-
- // Show modal for card scanning
- mHandler.obtainMessage(SHOW_MODAL_SCAN_CARD).sendToTarget();
-
- // Start scanning automatically
- startCardCheck();
- }
-
- private void restartAutoScanning() {
- Log.d(TAG, "Restarting auto-scanning after delay...");
-
- // Wait a bit before restarting
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- if (!isFinishing() && !isProcessing) {
- autoStartScanning();
- }
- }, 2000); // 2 second delay before restart
- }
-
- // ====== UPDATED CARD CHECK METHODS ======
- private void startCardCheck() {
- if (isProcessing) {
- Log.d(TAG, "Card check already in progress - ignoring call");
- return;
- }
-
- Log.d(TAG, "Starting auto card check - setting isProcessing = true");
- isProcessing = true;
-
- updateStatusUI("Initializing scanner...\n\nPlease wait...", true);
-
- try {
- if (mEMVOptV2 != null) {
- Log.d(TAG, "Forcing EMV reset before card check");
- mEMVOptV2.initEmvProcess();
-
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- if (isProcessing && !isFinishing()) {
- continueCardCheck();
- }
- }, 500);
-
- } else {
- continueCardCheck();
- }
-
- } catch (Exception e) {
- Log.e(TAG, "Error in startCardCheck: " + e.getMessage(), e);
- handleScanError("Error: " + e.getMessage());
- }
- }
-
- private void continueCardCheck() {
- try {
- Log.d(TAG, "continueCardCheck - isProcessing: " + isProcessing);
-
- if (!isProcessing) {
- Log.d(TAG, "continueCardCheck called but not processing - returning");
- return;
- }
-
- String mode = isEMVMode ? "EMV Mode" : "Simple Mode";
- updateStatusUI("Scanning for card...\n\nMode: " + mode + "\n\nPlease insert, swipe, or tap your card", true);
-
- if (isEMVMode) {
- startEMVCardCheck();
- } else {
- startSimpleCardCheck();
- }
-
- } catch (Exception e) {
- Log.e(TAG, "Error in continueCardCheck: " + e.getMessage(), e);
- handleScanError("Error: " + e.getMessage());
- }
- }
-
- // ====== UPDATED HANDLE SCAN ERROR ======
- private void handleScanError(String errorMessage) {
- Log.e(TAG, "Scan error: " + errorMessage);
-
- isProcessing = false;
- mProcessStep = 0;
-
- updateStatusUI("Scan error: " + errorMessage + "\n\nRetrying in 2 seconds...", false);
- showToast(errorMessage);
-
- // Hide modal on error
- hideModal();
-
- // Auto-restart scanning after error
- restartAutoScanning();
- }
-
- private void startEMVCardCheck() {
- try {
- if (mEMVOptV2 == null) {
- showToast("EMV not initialized");
- return;
- }
-
- mEMVOptV2.initEmvProcess();
- initEmvTlvData();
-
- updateStatusUI("EMV Mode: Starting card scan...\nPlease insert, swipe, or tap your card", true);
-
- int cardType = AidlConstantsV2.CardType.NFC.getValue() | AidlConstantsV2.CardType.IC.getValue();
- android.util.Log.d(TAG, "Starting EMV checkCard with cardType: " + cardType);
-
- MyApplication.app.readCardOptV2.checkCard(cardType, mEMVCheckCardCallback, 60);
-
- } catch (Exception e) {
- e.printStackTrace();
- android.util.Log.e(TAG, "Error in startEMVCardCheck: " + e.getMessage());
- showToast("Error starting EMV card scan: " + e.getMessage());
- }
- }
-
- private void startSimpleCardCheck() {
- try {
- if (!MyApplication.app.isConnectPaySDK()) {
- updateStatusUI("Connecting to PaySDK...", true);
- MyApplication.app.bindPaySDKService();
- return;
- }
-
- int cardType = CardType.MAGNETIC.getValue() | CardType.IC.getValue() | CardType.NFC.getValue();
- updateStatusUI("Simple Mode: Starting card scan...\nPlease insert, swipe, or tap your card", true);
-
- MyApplication.app.readCardOptV2.checkCard(cardType, mSimpleCheckCardCallback, 60);
-
- } catch (Exception e) {
- e.printStackTrace();
- showToast("Error starting card scan: " + e.getMessage());
- }
- }
-
- private void stopCardCheck() {
- try {
- if (MyApplication.app != null && MyApplication.app.readCardOptV2 != null) {
- MyApplication.app.readCardOptV2.cancelCheckCard();
- }
- isProcessing = false;
- updateStatusUI("Ready for card...\n\nPlease insert, swipe, or tap your card", true);
- } catch (Exception e) {
- android.util.Log.e(TAG, "Error stopping card check: " + e.getMessage());
- }
- }
-
- // ====== SIMPLE CARD DETECTION CALLBACK ======
- private final CheckCardCallbackV2 mSimpleCheckCardCallback = new CheckCardCallbackV2.Stub() {
- @Override
- public void findMagCard(Bundle info) throws RemoteException {
- android.util.Log.d(TAG, "Simple Mode: findMagCard callback triggered");
- runOnUiThread(() -> {
- showModalProcessing("Kartu Ditemukan - Memproses...");
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- handleSimpleCardResult(info, "MAGNETIC");
- }, 1500);
- });
- }
-
- @Override
- public void findICCard(String atr) throws RemoteException {
- Bundle info = new Bundle();
- info.putString("atr", atr);
- runOnUiThread(() -> {
- showModalProcessing("Kartu IC Ditemukan - Memproses...");
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- handleSimpleCardResult(info, "IC");
- }, 1500);
- });
- }
-
- @Override
- public void findRFCard(String uuid) throws RemoteException {
- Bundle info = new Bundle();
- info.putString("uuid", uuid);
- runOnUiThread(() -> {
- showModalProcessing("Kartu NFC Ditemukan - Memproses...");
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- handleSimpleCardResult(info, "NFC");
- }, 1500);
- });
- }
-
- @Override
- public void onError(int code, String message) throws RemoteException {
- runOnUiThread(() -> {
- showToast("Card error: " + message);
- hideModal();
- stopCardCheck();
- });
- }
-
- @Override
- public void findICCardEx(Bundle info) throws RemoteException {
- runOnUiThread(() -> {
- showModalProcessing("Kartu IC Ditemukan - Memproses...");
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- handleSimpleCardResult(info, "IC");
- }, 1500);
- });
- }
-
- @Override
- public void findRFCardEx(Bundle info) throws RemoteException {
- runOnUiThread(() -> {
- showModalProcessing("Kartu NFC Ditemukan - Memproses...");
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- handleSimpleCardResult(info, "NFC");
- }, 1500);
- });
- }
-
- @Override
- public void onErrorEx(Bundle info) throws RemoteException {
- runOnUiThread(() -> {
- String msg = info.getString("message", "Unknown error");
- showToast("Card error: " + msg);
- hideModal();
- stopCardCheck();
- });
- }
- };
-
- // ====== EMV CARD DETECTION CALLBACK ======
- private final CheckCardCallbackV2 mEMVCheckCardCallback = new CheckCardCallbackV2.Stub() {
- @Override
- public void findMagCard(Bundle info) throws RemoteException {
- runOnUiThread(() -> {
- showModalProcessing("Kartu Magnetik Ditemukan - Memproses...");
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- handleSimpleCardResult(info, "MAGNETIC");
- }, 1500);
- });
- }
-
- @Override
- public void findICCard(String atr) throws RemoteException {
- MyApplication.app.basicOptV2.buzzerOnDevice(1, 2750, 200, 0);
- mCardType = AidlConstantsV2.CardType.IC.getValue();
- runOnUiThread(() -> {
- showModalProcessing("Kartu IC Ditemukan - Memulai EMV...");
- startEMVTransactionProcess();
- });
- }
-
- @Override
- public void findRFCard(String uuid) throws RemoteException {
- mCardType = AidlConstantsV2.CardType.NFC.getValue();
- runOnUiThread(() -> {
- showModalProcessing("Kartu NFC Ditemukan - Memulai EMV...");
- startEMVTransactionProcess();
- });
- }
-
- @Override
- public void onError(int code, String message) throws RemoteException {
- runOnUiThread(() -> {
- showToast("EMV Error: " + message);
- hideModal();
- stopCardCheck();
- });
- }
-
- @Override
- public void findICCardEx(Bundle info) throws RemoteException {
- mCardType = AidlConstantsV2.CardType.IC.getValue();
- runOnUiThread(() -> {
- showModalProcessing("Kartu IC Ditemukan - Memulai EMV...");
- startEMVTransactionProcess();
- });
- }
-
- @Override
- public void findRFCardEx(Bundle info) throws RemoteException {
- mCardType = AidlConstantsV2.CardType.NFC.getValue();
- runOnUiThread(() -> {
- showModalProcessing("Kartu NFC Ditemukan - Memulai EMV...");
- startEMVTransactionProcess();
- });
- }
-
- @Override
- public void onErrorEx(Bundle info) throws RemoteException {
- runOnUiThread(() -> {
- String msg = info.getString("message", "Unknown error");
- showToast("EMV Error: " + msg);
- hideModal();
- stopCardCheck();
- });
- }
- };
-
- private void handleSimpleCardResult(Bundle info, String cardType) {
- // For simple mode, navigate directly to results
- Intent intent = new Intent(this, CreditCardActivity.class);
- intent.putExtra("TRANSACTION_AMOUNT", transactionAmount);
- intent.putExtra("CARD_TYPE", cardType);
- intent.putExtra("CARD_DATA", info);
- intent.putExtra("EMV_MODE", isEMVMode);
- startActivity(intent);
- finish();
- }
-
- private void startEMVTransactionProcess() {
- // Prevent multiple calls
- if (mProcessStep != 0) {
- Log.d(TAG, "EMV transaction already in progress (step: " + mProcessStep + ") - ignoring call");
- return;
- }
-
- android.util.Log.d(TAG, "Starting EMV transaction process");
- mProcessStep = 1; // Set step to prevent multiple calls
-
- try {
- // Ensure EMV is properly reset before starting
- mEMVOptV2.initEmvProcess();
-
- // Small delay to ensure reset is complete
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- try {
- // Double check we're still in the right state
- if (mProcessStep <= 0) {
- Log.d(TAG, "EMV process was cancelled - not starting");
- return;
- }
-
- Bundle bundle = new Bundle();
- bundle.putString("amount", transactionAmount);
- bundle.putString("transType", "00");
-
- if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) {
- bundle.putInt("flowType", AidlConstantsV2.EMV.FlowType.TYPE_EMV_STANDARD);
- } else {
- bundle.putInt("flowType", AidlConstantsV2.EMV.FlowType.TYPE_EMV_STANDARD);
- }
- bundle.putInt("cardType", mCardType);
-
- updateStatusUI("EMV processing started...\nReading card data...", true);
- showModalProcessing("Memproses Data Kartu EMV...");
-
- Log.d(TAG, "Starting transactProcessEx with reset EMV");
- mEMVOptV2.transactProcessEx(bundle, mEMVListener);
-
- } catch (Exception e) {
- Log.e(TAG, "Error in delayed EMV start: " + e.getMessage(), e);
- hideModal();
- resetScanningState();
- showToast("Error starting EMV: " + e.getMessage());
- }
- }, 300); // 300ms delay
-
- } catch (Exception e) {
- android.util.Log.e(TAG, "Error starting EMV transaction: " + e.getMessage());
- e.printStackTrace();
- hideModal();
- resetScanningState();
- showToast("Error starting EMV transaction: " + e.getMessage());
- }
- }
-
- // ====== EMV LISTENER ======
- private final EMVListenerV2 mEMVListener = new EMVListenerV2.Stub() {
-
- @Override
- public void onWaitAppSelect(List appNameList, boolean isFirstSelect) throws RemoteException {
- android.util.Log.d(TAG, "onWaitAppSelect isFirstSelect:" + isFirstSelect);
- mProcessStep = EMV_APP_SELECT;
- String[] candidateNames = getCandidateNames(appNameList);
- runOnUiThread(() -> hideModal());
- mHandler.obtainMessage(EMV_APP_SELECT, candidateNames).sendToTarget();
- }
-
- @Override
- public void onAppFinalSelect(String tag9F06Value) throws RemoteException {
- android.util.Log.d(TAG, "onAppFinalSelect tag9F06Value:" + tag9F06Value);
- mProcessStep = EMV_FINAL_APP_SELECT;
- mHandler.obtainMessage(EMV_FINAL_APP_SELECT, tag9F06Value).sendToTarget();
- }
-
- @Override
- public void onConfirmCardNo(String cardNo) throws RemoteException {
- android.util.Log.d(TAG, "onConfirmCardNo cardNo:" + maskCardNumber(cardNo));
- mCardNo = cardNo;
- mProcessStep = EMV_CONFIRM_CARD_NO;
- mHandler.obtainMessage(EMV_CONFIRM_CARD_NO).sendToTarget();
- }
-
- @Override
- public void onRequestShowPinPad(int pinType, int remainTime) throws RemoteException {
- android.util.Log.d(TAG, "onRequestShowPinPad pinType:" + pinType + " remainTime:" + remainTime);
- mPinType = pinType;
- if (mCardNo == null) {
- mCardNo = getCardNo();
- }
- mProcessStep = EMV_SHOW_PIN_PAD;
- mHandler.obtainMessage(EMV_SHOW_PIN_PAD).sendToTarget();
- }
-
- @Override
- public void onRequestSignature() throws RemoteException {
- android.util.Log.d(TAG, "onRequestSignature");
- mProcessStep = EMV_SIGNATURE;
- mHandler.obtainMessage(EMV_SIGNATURE).sendToTarget();
- }
-
- @Override
- public void onCertVerify(int certType, String certInfo) throws RemoteException {
- android.util.Log.d(TAG, "onCertVerify certType:" + certType + " certInfo:" + certInfo);
- mCertInfo = certInfo;
- mProcessStep = EMV_CERT_VERIFY;
- mHandler.obtainMessage(EMV_CERT_VERIFY).sendToTarget();
- }
-
- @Override
- public void onOnlineProc() throws RemoteException {
- android.util.Log.d(TAG, "onOnlineProcess");
- mProcessStep = EMV_ONLINE_PROCESS;
- mHandler.obtainMessage(EMV_ONLINE_PROCESS).sendToTarget();
- }
-
- @Override
- public void onCardDataExchangeComplete() throws RemoteException {
- android.util.Log.d(TAG, "onCardDataExchangeComplete");
- if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) {
- MyApplication.app.basicOptV2.buzzerOnDevice(1, 2750, 200, 0);
- }
- }
-
- @Override
- public void onTransResult(int code, String desc) throws RemoteException {
- if (mCardNo == null) {
- mCardNo = getCardNo();
- }
- android.util.Log.d(TAG, "onTransResult code:" + code + " desc:" + desc);
-
- if (code == 1 || code == 2 || code == 5 || code == 6) {
- mHandler.obtainMessage(EMV_TRANS_SUCCESS, code, code, desc).sendToTarget();
- } else {
- mHandler.obtainMessage(EMV_TRANS_FAIL, code, code, desc).sendToTarget();
- }
- }
-
- @Override
- public void onConfirmationCodeVerified() throws RemoteException {
- android.util.Log.d(TAG, "onConfirmationCodeVerified");
- }
-
- @Override
- public void onRequestDataExchange(String cardNo) throws RemoteException {
- android.util.Log.d(TAG, "onRequestDataExchange,cardNo:" + cardNo);
- mEMVOptV2.importDataExchangeStatus(0);
- }
-
- @Override
- public void onTermRiskManagement() throws RemoteException {
- android.util.Log.d(TAG, "onTermRiskManagement");
- mEMVOptV2.importTermRiskManagementStatus(0);
- }
-
- @Override
- public void onPreFirstGenAC() throws RemoteException {
- android.util.Log.d(TAG, "onPreFirstGenAC");
- mEMVOptV2.importPreFirstGenACStatus(0);
- }
-
- @Override
- public void onDataStorageProc(String[] containerID, String[] containerContent) throws RemoteException {
- android.util.Log.d(TAG, "onDataStorageProc");
- String[] tags = new String[0];
- String[] values = new String[0];
- mEMVOptV2.importDataStorage(tags, values);
- }
- };
-
- // ====== EMV DIALOG HANDLERS ======
- private void showAppSelectDialog(String[] candidateNames) {
- mAppSelectDialog = new AlertDialog.Builder(this)
- .setTitle("Please select application")
- .setNegativeButton("Cancel", (dialog, which) -> importAppSelect(-1))
- .setPositiveButton("OK", (dialog, which) -> importAppSelect(mSelectIndex))
- .setSingleChoiceItems(candidateNames, 0, (dialog, which) -> mSelectIndex = which)
- .create();
- mSelectIndex = 0;
- mAppSelectDialog.show();
- }
-
- // ====== UPDATED EMV DIALOG HANDLERS ======
- private void showCardNumberConfirmation() {
- runOnUiThread(() -> {
- String displayText = "Card Number Detected:\n" + maskCardNumber(mCardNo) +
- "\n\nConfirming automatically...";
- updateStatusUI(displayText, true);
- showModalProcessing("Mengkonfirmasi Nomor Kartu...");
-
- // Auto-confirm after short delay
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- Log.d(TAG, "Auto-confirming card number");
- importCardNoStatus(0);
- }, 1500);
-
- mProcessStep = EMV_CONFIRM_CARD_NO;
- });
- }
-
- private void showCertificateVerification() {
- runOnUiThread(() -> {
- String displayText = "Certificate Verification:\n" + mCertInfo +
- "\n\nProcessing automatically...";
- updateStatusUI(displayText, true);
- showModalProcessing("Memverifikasi Sertifikat...");
-
- // Auto-confirm after short delay
- new Handler(Looper.getMainLooper()).postDelayed(() -> {
- Log.d(TAG, "Auto-confirming certificate");
- importCertStatus(0);
- }, 1500);
-
- mProcessStep = EMV_CERT_VERIFY;
- });
- }
-
- // ====== UPDATED PIN PAD METHODS ======
- private void initPinPad() {
- android.util.Log.e(TAG, "========== PIN PAD INITIALIZATION ==========");
- try {
- if (mPinPadOptV2 == null) {
- throw new IllegalStateException("PIN Pad service not available");
- }
-
- if (mCardNo == null || mCardNo.length() < 13) {
- throw new IllegalArgumentException("Invalid card number for PIN");
- }
-
- PinPadConfigV2 pinPadConfig = new PinPadConfigV2();
- pinPadConfig.setPinPadType(0);
- pinPadConfig.setPinType(mPinType);
- pinPadConfig.setOrderNumKey(true); // Set to true for normal order, false for random
-
- String panForPin = mCardNo.substring(mCardNo.length() - 13, mCardNo.length() - 1);
- byte[] panBytes = panForPin.getBytes("US-ASCII");
- pinPadConfig.setPan(panBytes);
-
- pinPadConfig.setTimeout(60 * 1000);
- pinPadConfig.setPinKeyIndex(12);
- pinPadConfig.setMaxInput(12);
- pinPadConfig.setMinInput(0);
- pinPadConfig.setKeySystem(0);
- pinPadConfig.setAlgorithmType(0);
-
- updateStatusUI("PIN Input Required\n\nPIN pad is ready\nPlease enter your PIN on the device", true);
-
- mPinPadOptV2.initPinPad(pinPadConfig, mPinPadListener);
-
- } catch (Exception e) {
- android.util.Log.e(TAG, "PIN pad initialization failed: " + e.getMessage());
- mHandler.obtainMessage(PIN_ERROR, -1, -1,
- "PIN Error: " + e.getMessage()).sendToTarget();
- }
- }
-
- // ====== UPDATED PIN PAD LISTENER ======
- private final PinPadListenerV2 mPinPadListener = new PinPadListenerV2.Stub() {
- @Override
- public void onPinLength(int len) throws RemoteException {
- android.util.Log.d(TAG, "PIN input length: " + len);
- runOnUiThread(() -> {
- String dots = "";
- for (int i = 0; i < len; i++) {
- dots += "• ";
- }
- updateStatusUI("PIN Input: " + dots + "\n\nEntering PIN... (" + len + " digits)", true);
- });
- }
-
- @Override
- public void onConfirm(int i, byte[] pinBlock) throws RemoteException {
- android.util.Log.d(TAG, "PIN input confirmed");
-
- runOnUiThread(() -> {
- updateStatusUI("PIN confirmed, processing...", true);
- showModalProcessing("PIN Dikonfirmasi - Memproses...");
- });
-
- if (pinBlock != null) {
- String hexStr = ByteUtil.bytes2HexStr(pinBlock);
- android.util.Log.d(TAG, "PIN block received: " + hexStr);
- mHandler.obtainMessage(PIN_CLICK_PIN, pinBlock).sendToTarget();
- } else {
- android.util.Log.d(TAG, "PIN bypass confirmed");
- mHandler.obtainMessage(PIN_CLICK_CONFIRM).sendToTarget();
- }
- }
-
- @Override
- public void onCancel() throws RemoteException {
- android.util.Log.d(TAG, "PIN input cancelled by user");
-
- runOnUiThread(() -> {
- updateStatusUI("PIN input cancelled", false);
- hideModal();
- });
-
- mHandler.obtainMessage(PIN_CLICK_CANCEL).sendToTarget();
- }
-
- @Override
- public void onError(int code) throws RemoteException {
- android.util.Log.e(TAG, "PIN pad error: " + code);
- String msg = AidlErrorCodeV2.valueOf(code).getMsg();
-
- runOnUiThread(() -> {
- updateStatusUI("PIN error: " + msg, false);
- hideModal();
- });
-
- mHandler.obtainMessage(PIN_ERROR, code, code, msg).sendToTarget();
- }
-
- @Override
- public void onHover(int event, byte[] data) throws RemoteException {
- android.util.Log.d(TAG, "PIN pad hover event: " + event);
- }
- };
-
- // ====== EMV RESET METHODS ======
- private void resetEMVAndRetry() {
- new Thread(() -> {
- try {
- Log.d(TAG, "Resetting EMV process...");
-
- // Cancel any existing card operations first
- if (MyApplication.app != null && MyApplication.app.readCardOptV2 != null) {
- MyApplication.app.readCardOptV2.cancelCheckCard();
- }
-
- Thread.sleep(500); // Wait for cancel to complete
-
- // Reset EMV process
- if (mEMVOptV2 != null) {
- mEMVOptV2.initEmvProcess(); // Reset EMV state
- }
-
- Thread.sleep(1500); // Wait longer for complete reset
-
- runOnUiThread(() -> {
- Log.d(TAG, "EMV reset complete - auto-restarting scan");
- isProcessing = false;
- mProcessStep = 0;
-
- updateStatusUI("EMV reset complete\n\nRestarting auto-scan...", true);
- showToast("EMV reset - restarting scan");
-
- // Auto-restart scanning
- restartAutoScanning();
- });
-
- } catch (Exception e) {
- Log.e(TAG, "Error resetting EMV: " + e.getMessage(), e);
- runOnUiThread(() -> {
- handleScanError("Reset failed - " + e.getMessage());
- });
- }
- }).start();
- }
-
- // ====== UPDATED RESET SCANNING STATE ======
- private void resetScanningState() {
- Log.d(TAG, "Resetting scanning state for auto-scan mode");
- isProcessing = false;
- mProcessStep = 0;
-
- updateStatusUI("Ready for card...\n\nPlease insert, swipe, or tap your card", true);
- hideModal();
- }
-
- // ====== HELPER METHODS ======
- private void navigateToResults() {
- Intent intent = new Intent(this, CreditCardActivity.class);
- intent.putExtra("TRANSACTION_AMOUNT", transactionAmount);
- intent.putExtra("CARD_TYPE", mCardType == AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC");
- intent.putExtra("EMV_MODE", isEMVMode);
- intent.putExtra("CARD_NO", mCardNo);
- startActivity(intent);
- finish();
- }
-
- // ====== UPDATED MOCK ONLINE PROCESS ======
- private void mockOnlineProcess() {
- new Thread(() -> {
- try {
- runOnUiThread(() -> updateStatusUI("Processing online authorization...", true));
- Thread.sleep(2000);
-
- try {
- String[] tags = {"71", "72", "91", "8A", "89"};
- String[] values = {"", "", "", "", ""};
- byte[] out = new byte[1024];
- int len = mEMVOptV2.importOnlineProcStatus(0, tags, values, out);
- if (len < 0) {
- android.util.Log.e(TAG, "importOnlineProcessStatus error,code:" + len);
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- }).start();
- }
-
- private String getCardNo() {
- // Implementation from original code
- return "";
- }
-
- private String maskCardNumber(String cardNo) {
- if (cardNo == null || cardNo.length() < 8) {
- return cardNo;
- }
- String first4 = cardNo.substring(0, 4);
- String last4 = cardNo.substring(cardNo.length() - 4);
- StringBuilder middle = new StringBuilder();
- for (int i = 0; i < cardNo.length() - 8; i++) {
- middle.append("*");
- }
- return first4 + middle.toString() + last4;
- }
-
- private String[] getCandidateNames(List candiList) {
- if (candiList == null || candiList.size() == 0) return new String[0];
- String[] result = new String[candiList.size()];
- for (int i = 0; i < candiList.size(); i++) {
- EMVCandidateV2 candi = candiList.get(i);
- String name = candi.appPreName;
- name = TextUtils.isEmpty(name) ? candi.appLabel : name;
- name = TextUtils.isEmpty(name) ? candi.appName : name;
- name = TextUtils.isEmpty(name) ? "" : name;
- result[i] = name;
- }
- return result;
- }
-
- // ====== EMV IMPORT METHODS ======
- private void importAppSelect(int selectIndex) {
- try {
- mEMVOptV2.importAppSelect(selectIndex);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private void importFinalAppSelectStatus(int status) {
- try {
- mEMVOptV2.importAppFinalSelectStatus(status);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
-
- private void importCardNoStatus(int status) {
- try {
- mEMVOptV2.importCardNoStatus(status);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private void importCertStatus(int status) {
- try {
- mEMVOptV2.importCertStatus(status);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private void importPinInputStatus(int inputResult) {
- try {
- mEMVOptV2.importPinInputStatus(mPinType, inputResult);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private void importSignatureStatus(int status) {
- try {
- mEMVOptV2.importSignatureStatus(status);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-
- private void showToast(String message) {
- Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
- }
-
- // ====== UPDATED ON SUPPORT NAVIGATE UP ======
- @Override
- public boolean onSupportNavigateUp() {
- // Handle back button press - clean up and go back
- try {
- // Cancel any ongoing operations
- if (MyApplication.app != null && MyApplication.app.readCardOptV2 != null) {
- MyApplication.app.readCardOptV2.cancelCheckCard();
- }
-
- // Reset EMV process
- if (mEMVOptV2 != null) {
- mEMVOptV2.initEmvProcess();
- }
-
- isProcessing = false;
- mProcessStep = 0;
-
- hideModal();
-
- } catch (Exception e) {
- Log.e(TAG, "Error cleaning up on back press: " + e.getMessage());
- }
-
- finish();
- return true;
- }
-
- @Override
- protected void onDestroy() {
- Log.d(TAG, "onDestroy - cleaning up auto-scan resources");
-
- // Stop auto-scanning
- isProcessing = false;
-
- try {
- // Cancel card operations
- if (MyApplication.app != null && MyApplication.app.readCardOptV2 != null) {
- MyApplication.app.readCardOptV2.cancelCheckCard();
- }
-
- // Reset EMV process
- if (mEMVOptV2 != null) {
- mEMVOptV2.initEmvProcess();
- }
-
- hideModal();
-
- } catch (Exception e) {
- Log.e(TAG, "Error cleaning up EMV: " + e.getMessage());
- }
-
- super.onDestroy();
- }
-
- @Override
- protected void onPause() {
- super.onPause();
- Log.d(TAG, "onPause - pausing auto-scan");
-
- // Don't stop scanning on pause - let it continue in background
- // This allows card detection even if screen goes off briefly
- }
-
- @Override
- protected void onResume() {
- super.onResume();
- Log.d(TAG, "onResume - resuming auto-scan");
-
- // If we're not processing and not finishing, restart auto-scan
- if (!isProcessing && !isFinishing() && mProcessStep == 0) {
- restartAutoScanning();
- }
- }
-}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bdkipoc/transaction/CreateTransactionActivity.java b/app/src/main/java/com/example/bdkipoc/transaction/CreateTransactionActivity.java
new file mode 100644
index 0000000..6b1386e
--- /dev/null
+++ b/app/src/main/java/com/example/bdkipoc/transaction/CreateTransactionActivity.java
@@ -0,0 +1,504 @@
+package com.example.bdkipoc.transaction;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+import android.view.View;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.appcompat.app.AlertDialog;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+import com.example.bdkipoc.R;
+import com.example.bdkipoc.transaction.managers.CardScannerManager;
+import com.example.bdkipoc.transaction.managers.EMVManager;
+import com.example.bdkipoc.transaction.managers.ModalManager;
+import com.example.bdkipoc.transaction.managers.PinPadManager;
+
+import java.text.NumberFormat;
+import java.util.Locale;
+
+import com.example.bdkipoc.kredit.CreditCardActivity;
+
+/**
+ * CreateTransactionActivity - Refactored with Manager Classes
+ * Handles amount input and card scanning in one screen
+ * Located in transaction package
+ */
+public class CreateTransactionActivity extends AppCompatActivity implements
+ EMVManager.EMVManagerCallback,
+ CardScannerManager.CardScannerCallback,
+ PinPadManager.PinPadManagerCallback {
+
+ private static final String TAG = "CreateTransaction";
+
+ // UI Components - Amount Input
+ private TextView tvAmountDisplay;
+ private TextView tvModeIndicator;
+ private Button btnConfirm;
+ private Button btnToggleMode;
+ private ProgressBar progressBar;
+
+ // Amount Input Keypad
+ private Button btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn0, btn00, btnClear;
+
+ // State Management
+ private String transactionAmount = "0";
+ private boolean isEMVMode = true;
+
+ // Manager Classes
+ private EMVManager emvManager;
+ private CardScannerManager cardScannerManager;
+ private PinPadManager pinPadManager;
+ private ModalManager modalManager;
+
+ // EMV Dialog
+ private AlertDialog mAppSelectDialog;
+ private int mSelectIndex;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_create_transaction);
+
+ initViews();
+ initManagers();
+ setupListeners();
+ updateAmountDisplay();
+ updateModeDisplay();
+ }
+
+ private void initViews() {
+ // Setup Toolbar
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+ if (getSupportActionBar() != null) {
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setTitle("Input Nominal Transaksi");
+ }
+
+ // Initialize amount input UI components
+ tvAmountDisplay = findViewById(R.id.tv_amount_display);
+ tvModeIndicator = findViewById(R.id.tv_mode_indicator);
+ btnConfirm = findViewById(R.id.btn_confirm);
+ btnToggleMode = findViewById(R.id.btn_toggle_mode);
+ progressBar = findViewById(R.id.progress_bar);
+
+ // Initialize keypad buttons
+ btn1 = findViewById(R.id.btn_1);
+ btn2 = findViewById(R.id.btn_2);
+ btn3 = findViewById(R.id.btn_3);
+ btn4 = findViewById(R.id.btn_4);
+ btn5 = findViewById(R.id.btn_5);
+ btn6 = findViewById(R.id.btn_6);
+ btn7 = findViewById(R.id.btn_7);
+ btn8 = findViewById(R.id.btn_8);
+ btn9 = findViewById(R.id.btn_9);
+ btn0 = findViewById(R.id.btn_0);
+ btn00 = findViewById(R.id.btn_00);
+ btnClear = findViewById(R.id.btn_clear);
+ }
+
+ private void initManagers() {
+ // Initialize Modal Manager
+ FrameLayout modalOverlay = findViewById(R.id.modal_overlay);
+ TextView modalText = findViewById(R.id.modal_text);
+ ImageView modalIcon = findViewById(R.id.modal_icon);
+ modalManager = new ModalManager(modalOverlay, modalText, modalIcon);
+
+ // Initialize other managers
+ emvManager = new EMVManager(this);
+ cardScannerManager = new CardScannerManager(this);
+ pinPadManager = new PinPadManager(this);
+
+ // Initialize EMV data
+ emvManager.initEMVData();
+
+ Log.d(TAG, "All managers initialized");
+ }
+
+ private void setupListeners() {
+ // Keypad number listeners
+ View.OnClickListener numberClickListener = v -> {
+ Button btn = (Button) v;
+ String number = btn.getText().toString();
+ appendToAmount(number);
+ };
+
+ btn1.setOnClickListener(numberClickListener);
+ btn2.setOnClickListener(numberClickListener);
+ btn3.setOnClickListener(numberClickListener);
+ btn4.setOnClickListener(numberClickListener);
+ btn5.setOnClickListener(numberClickListener);
+ btn6.setOnClickListener(numberClickListener);
+ btn7.setOnClickListener(numberClickListener);
+ btn8.setOnClickListener(numberClickListener);
+ btn9.setOnClickListener(numberClickListener);
+ btn0.setOnClickListener(numberClickListener);
+ btn00.setOnClickListener(numberClickListener);
+
+ // Clear button
+ btnClear.setOnClickListener(v -> clearAmount());
+
+ // Confirm button - shows modal and starts scanning
+ btnConfirm.setOnClickListener(v -> handleConfirmAmount());
+
+ // Toggle mode button
+ btnToggleMode.setOnClickListener(v -> toggleEMVMode());
+
+ // Modal overlay click to close (only if not processing)
+ findViewById(R.id.modal_overlay).setOnClickListener(v -> {
+ if (modalManager.isShowing() && !cardScannerManager.isScanning()) {
+ modalManager.hideModal();
+ }
+ });
+ }
+
+ // ====== AMOUNT INPUT METHODS ======
+ private void appendToAmount(String number) {
+ String currentAmount = transactionAmount.equals("0") ? "" : transactionAmount;
+ String newAmount = currentAmount + number;
+
+ if (newAmount.length() <= 9) {
+ transactionAmount = newAmount;
+ updateAmountDisplay();
+ }
+ }
+
+ private void clearAmount() {
+ if (transactionAmount.length() > 1) {
+ transactionAmount = transactionAmount.substring(0, transactionAmount.length() - 1);
+ } else {
+ transactionAmount = "0";
+ }
+ updateAmountDisplay();
+ }
+
+ private void updateAmountDisplay() {
+ if (tvAmountDisplay != null) {
+ long amountCents = Long.parseLong(transactionAmount);
+ double amountRupiah = amountCents / 100.0;
+ NumberFormat formatter = NumberFormat.getCurrencyInstance(new Locale("id", "ID"));
+ String formattedAmount = formatter.format(amountRupiah);
+ tvAmountDisplay.setText(formattedAmount);
+ }
+ }
+
+ private void handleConfirmAmount() {
+ long amountCents = Long.parseLong(transactionAmount);
+ if (amountCents < 100) { // Minimum Rp 1.00
+ showToast("Minimum amount is Rp 1.00");
+ return;
+ }
+
+ // Show modal and start card scanning
+ showModalAndStartScanning();
+ }
+
+ private void showModalAndStartScanning() {
+ Log.d(TAG, "Starting card scanning with modal...");
+
+ // Show modal first
+ modalManager.showScanCardModal();
+
+ // Start scanning after short delay
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ cardScannerManager.startScanning(isEMVMode);
+ }, 1000);
+ }
+
+ private void toggleEMVMode() {
+ isEMVMode = !isEMVMode;
+ updateModeDisplay();
+ showToast("Mode switched to: " + (isEMVMode ? "EMV Mode" : "Simple Mode"));
+ }
+
+ private void updateModeDisplay() {
+ String mode = isEMVMode ? "EMV Mode (Full Card Data)" : "Simple Mode (Basic Detection)";
+ if (tvModeIndicator != null) {
+ tvModeIndicator.setText("Current Mode: " + mode);
+ }
+ if (btnToggleMode != null) {
+ btnToggleMode.setText(isEMVMode ? "Switch to Simple" : "Switch to EMV");
+ }
+ }
+
+ // ====== CARD SCANNER CALLBACK METHODS ======
+ @Override
+ public void onCardDetected(String cardType, Bundle cardData) {
+ Log.d(TAG, "Simple card detected: " + cardType);
+ modalManager.showProcessingModal("Kartu " + cardType + " Ditemukan - Memproses...");
+
+ // Navigate to results after short delay
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ navigateToResults(cardType, cardData, null);
+ }, 1500);
+ }
+
+ @Override
+ public void onEMVCardDetected(int cardType) {
+ Log.d(TAG, "EMV card detected: " + cardType);
+ modalManager.showProcessingModal("Kartu Ditemukan - Memulai EMV...");
+
+ // Start EMV transaction process
+ emvManager.startEMVTransaction(transactionAmount, cardType);
+ }
+
+ @Override
+ public void onScanError(String errorMessage) {
+ Log.e(TAG, "Scan error: " + errorMessage);
+ showToast(errorMessage);
+ modalManager.hideModal();
+
+ // Auto-restart scanning after error
+ restartScanningAfterDelay();
+ }
+
+ @Override
+ public void onScanProgress(String message) {
+ Log.d(TAG, "Scan progress: " + message);
+ // Can update UI with progress if needed
+ }
+
+ // ====== EMV MANAGER CALLBACK METHODS ======
+ @Override
+ public void onAppSelect(String[] candidateNames) {
+ modalManager.hideModal();
+ showAppSelectDialog(candidateNames);
+ }
+
+ @Override
+ public void onFinalAppSelect() {
+ emvManager.importFinalAppSelectStatus(0);
+ }
+
+ @Override
+ public void onConfirmCardNo(String cardNo) {
+ modalManager.showProcessingModal("Mengkonfirmasi Nomor Kartu...");
+
+ // Auto-confirm after short delay
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ Log.d(TAG, "Auto-confirming card number");
+ emvManager.importCardNoStatus(0);
+ }, 1500);
+ }
+
+ @Override
+ public void onCertVerify(String certInfo) {
+ modalManager.showProcessingModal("Memverifikasi Sertifikat...");
+
+ // Auto-confirm after short delay
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ Log.d(TAG, "Auto-confirming certificate");
+ emvManager.importCertStatus(0);
+ }, 1500);
+ }
+
+ @Override
+ public void onShowPinPad(int pinType) {
+ Log.d(TAG, "Initializing PIN pad...");
+ modalManager.hideModal();
+
+ String cardNo = emvManager.getCardNo();
+ if (cardNo != null && cardNo.length() >= 13) {
+ pinPadManager.initPinPad(cardNo, pinType);
+ } else {
+ showToast("Invalid card number for PIN");
+ emvManager.importPinInputStatus(3); // Error
+ }
+ }
+
+ @Override
+ public void onOnlineProcess() {
+ modalManager.showProcessingModal("Memproses Otorisasi Online...");
+ emvManager.mockOnlineProcess();
+ }
+
+ @Override
+ public void onSignature() {
+ emvManager.importSignatureStatus(0);
+ }
+
+ @Override
+ public void onTransactionSuccess(int code, String desc) {
+ Log.d(TAG, "EMV Transaction successful");
+ modalManager.hideModal();
+
+ String cardType = emvManager.getCardType() == com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
+ navigateToResults(cardType, null, emvManager.getCardNo());
+ }
+
+ @Override
+ public void onTransactionFailed(int code, String desc) {
+ Log.e(TAG, "EMV Transaction failed: " + desc + " (Code: " + code + ")");
+
+ modalManager.hideModal();
+ if (code == -50009) {
+ // EMV process conflict - reset and retry
+ Log.d(TAG, "EMV process conflict detected - resetting...");
+ showToast("EMV busy, resetting...");
+ resetEMVAndRetry();
+ } else {
+ // Other errors - show message and restart scanning
+ showToast("Transaction failed: " + desc);
+ restartScanningAfterDelay();
+ }
+ }
+
+ // ====== PIN PAD CALLBACK METHODS ======
+ @Override
+ public void onPinInputLength(int length) {
+ String dots = "";
+ for (int i = 0; i < length; i++) {
+ dots += "• ";
+ }
+ // Can show PIN input feedback on UI if needed
+ Log.d(TAG, "PIN input length: " + length);
+ }
+
+ @Override
+ public void onPinInputConfirmed(byte[] pinBlock) {
+ modalManager.showProcessingModal("PIN Dikonfirmasi - Memproses...");
+
+ if (pinBlock != null) {
+ emvManager.importPinInputStatus(0); // PIN entered
+ } else {
+ emvManager.importPinInputStatus(2); // PIN bypassed
+ }
+ }
+
+ @Override
+ public void onPinInputCancelled() {
+ showToast("PIN input cancelled by user");
+ modalManager.hideModal();
+ emvManager.importPinInputStatus(1); // Cancelled
+ }
+
+ @Override
+ public void onPinInputError(int code, String message) {
+ Log.e(TAG, "PIN Error: " + message);
+ showToast("PIN Error: " + message);
+ modalManager.hideModal();
+ emvManager.importPinInputStatus(3); // Error
+ }
+
+ // ====== HELPER METHODS ======
+ private void showAppSelectDialog(String[] candidateNames) {
+ mAppSelectDialog = new AlertDialog.Builder(this)
+ .setTitle("Please select application")
+ .setNegativeButton("Cancel", (dialog, which) -> emvManager.importAppSelect(-1))
+ .setPositiveButton("OK", (dialog, which) -> emvManager.importAppSelect(mSelectIndex))
+ .setSingleChoiceItems(candidateNames, 0, (dialog, which) -> mSelectIndex = which)
+ .create();
+ mSelectIndex = 0;
+ mAppSelectDialog.show();
+ }
+
+ private void navigateToResults(String cardType, Bundle cardData, String cardNo) {
+ modalManager.hideModal();
+
+ Intent intent = new Intent(this, CreditCardActivity.class);
+ intent.putExtra("TRANSACTION_AMOUNT", transactionAmount);
+ intent.putExtra("CARD_TYPE", cardType);
+ intent.putExtra("EMV_MODE", isEMVMode);
+
+ if (cardData != null) {
+ intent.putExtra("CARD_DATA", cardData);
+ }
+ if (cardNo != null) {
+ intent.putExtra("CARD_NO", cardNo);
+ }
+
+ startActivity(intent);
+ finish();
+ }
+
+ private void restartScanningAfterDelay() {
+ Log.d(TAG, "Restarting scanning after delay...");
+
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ if (!isFinishing()) {
+ showModalAndStartScanning();
+ }
+ }, 2000);
+ }
+
+ private void resetEMVAndRetry() {
+ new Thread(() -> {
+ try {
+ Log.d(TAG, "Resetting EMV process...");
+
+ // Cancel any existing operations
+ cardScannerManager.stopScanning();
+ Thread.sleep(500);
+
+ // Reset EMV process
+ emvManager.resetEMVProcess();
+ Thread.sleep(1500);
+
+ runOnUiThread(() -> {
+ Log.d(TAG, "EMV reset complete - auto-restarting scan");
+ showToast("EMV reset - restarting scan");
+ restartScanningAfterDelay();
+ });
+
+ } catch (Exception e) {
+ Log.e(TAG, "Error resetting EMV: " + e.getMessage(), e);
+ runOnUiThread(() -> {
+ showToast("Reset failed - " + e.getMessage());
+ restartScanningAfterDelay();
+ });
+ }
+ }).start();
+ }
+
+ private void showToast(String message) {
+ Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public boolean onSupportNavigateUp() {
+ cleanup();
+ finish();
+ return true;
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.d(TAG, "onDestroy - cleaning up resources");
+ cleanup();
+ super.onDestroy();
+ }
+
+ private void cleanup() {
+ try {
+ // Stop all operations
+ if (cardScannerManager != null) {
+ cardScannerManager.stopScanning();
+ }
+
+ if (emvManager != null) {
+ emvManager.resetEMVProcess();
+ }
+
+ if (pinPadManager != null) {
+ pinPadManager.cancelPinInput();
+ }
+
+ if (modalManager != null) {
+ modalManager.hideModal();
+ }
+
+ } catch (Exception e) {
+ Log.e(TAG, "Error during cleanup: " + e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bdkipoc/transaction/managers/CardScannerManager.java b/app/src/main/java/com/example/bdkipoc/transaction/managers/CardScannerManager.java
new file mode 100644
index 0000000..39889da
--- /dev/null
+++ b/app/src/main/java/com/example/bdkipoc/transaction/managers/CardScannerManager.java
@@ -0,0 +1,258 @@
+package com.example.bdkipoc.transaction.managers;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.example.bdkipoc.MyApplication;
+import com.sunmi.pay.hardware.aidl.AidlConstants.CardType;
+import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2;
+import com.sunmi.pay.hardware.aidlv2.readcard.CheckCardCallbackV2;
+
+/**
+ * CardScannerManager - Handles card detection for both EMV and Simple modes
+ */
+public class CardScannerManager {
+ private static final String TAG = "CardScannerManager";
+
+ private CardScannerCallback callback;
+ private boolean isProcessing = false;
+
+ public interface CardScannerCallback {
+ void onCardDetected(String cardType, Bundle cardData);
+ void onEMVCardDetected(int cardType);
+ void onScanError(String errorMessage);
+ void onScanProgress(String message);
+ }
+
+ public CardScannerManager(CardScannerCallback callback) {
+ this.callback = callback;
+ }
+
+ public void startScanning(boolean isEMVMode) {
+ if (isProcessing) {
+ Log.d(TAG, "Card check already in progress - ignoring call");
+ return;
+ }
+
+ Log.d(TAG, "Starting card check - setting isProcessing = true");
+ isProcessing = true;
+
+ try {
+ // Small delay to ensure everything is ready
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ if (isProcessing) {
+ if (isEMVMode) {
+ startEMVCardCheck();
+ } else {
+ startSimpleCardCheck();
+ }
+ }
+ }, 500);
+
+ } catch (Exception e) {
+ Log.e(TAG, "Error in startScanning: " + e.getMessage(), e);
+ handleScanError("Error: " + e.getMessage());
+ }
+ }
+
+ public void stopScanning() {
+ try {
+ if (MyApplication.app != null && MyApplication.app.readCardOptV2 != null) {
+ MyApplication.app.readCardOptV2.cancelCheckCard();
+ }
+ isProcessing = false;
+ Log.d(TAG, "Card scanning stopped");
+ } catch (Exception e) {
+ Log.e(TAG, "Error stopping card check: " + e.getMessage());
+ }
+ }
+
+ public boolean isScanning() {
+ return isProcessing;
+ }
+
+ private void startEMVCardCheck() {
+ try {
+ if (callback != null) {
+ callback.onScanProgress("EMV Mode: Starting card scan...");
+ }
+
+ int cardType = AidlConstantsV2.CardType.NFC.getValue() | AidlConstantsV2.CardType.IC.getValue();
+ Log.d(TAG, "Starting EMV checkCard with cardType: " + cardType);
+
+ MyApplication.app.readCardOptV2.checkCard(cardType, mEMVCheckCardCallback, 60);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ Log.e(TAG, "Error in startEMVCardCheck: " + e.getMessage());
+ handleScanError("Error starting EMV card scan: " + e.getMessage());
+ }
+ }
+
+ private void startSimpleCardCheck() {
+ try {
+ if (!MyApplication.app.isConnectPaySDK()) {
+ if (callback != null) {
+ callback.onScanProgress("Connecting to PaySDK...");
+ }
+ MyApplication.app.bindPaySDKService();
+ return;
+ }
+
+ if (callback != null) {
+ callback.onScanProgress("Simple Mode: Starting card scan...");
+ }
+
+ int cardType = CardType.MAGNETIC.getValue() | CardType.IC.getValue() | CardType.NFC.getValue();
+
+ MyApplication.app.readCardOptV2.checkCard(cardType, mSimpleCheckCardCallback, 60);
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ handleScanError("Error starting card scan: " + e.getMessage());
+ }
+ }
+
+ private void handleScanError(String errorMessage) {
+ Log.e(TAG, "Scan error: " + errorMessage);
+ isProcessing = false;
+
+ if (callback != null) {
+ callback.onScanError(errorMessage);
+ }
+ }
+
+ public void resetScanning() {
+ Log.d(TAG, "Resetting scanning state");
+ isProcessing = false;
+ }
+
+ // Simple Card Detection Callback
+ private final CheckCardCallbackV2 mSimpleCheckCardCallback = new CheckCardCallbackV2.Stub() {
+ @Override
+ public void findMagCard(Bundle info) throws RemoteException {
+ Log.d(TAG, "Simple Mode: findMagCard callback triggered");
+ isProcessing = false;
+ if (callback != null) {
+ callback.onCardDetected("MAGNETIC", info);
+ }
+ }
+
+ @Override
+ public void findICCard(String atr) throws RemoteException {
+ Bundle info = new Bundle();
+ info.putString("atr", atr);
+ isProcessing = false;
+ if (callback != null) {
+ callback.onCardDetected("IC", info);
+ }
+ }
+
+ @Override
+ public void findRFCard(String uuid) throws RemoteException {
+ Bundle info = new Bundle();
+ info.putString("uuid", uuid);
+ isProcessing = false;
+ if (callback != null) {
+ callback.onCardDetected("NFC", info);
+ }
+ }
+
+ @Override
+ public void onError(int code, String message) throws RemoteException {
+ isProcessing = false;
+ if (callback != null) {
+ callback.onScanError("Card error: " + message);
+ }
+ }
+
+ @Override
+ public void findICCardEx(Bundle info) throws RemoteException {
+ isProcessing = false;
+ if (callback != null) {
+ callback.onCardDetected("IC", info);
+ }
+ }
+
+ @Override
+ public void findRFCardEx(Bundle info) throws RemoteException {
+ isProcessing = false;
+ if (callback != null) {
+ callback.onCardDetected("NFC", info);
+ }
+ }
+
+ @Override
+ public void onErrorEx(Bundle info) throws RemoteException {
+ isProcessing = false;
+ String msg = info.getString("message", "Unknown error");
+ if (callback != null) {
+ callback.onScanError("Card error: " + msg);
+ }
+ }
+ };
+
+ // EMV Card Detection Callback
+ private final CheckCardCallbackV2 mEMVCheckCardCallback = new CheckCardCallbackV2.Stub() {
+ @Override
+ public void findMagCard(Bundle info) throws RemoteException {
+ isProcessing = false;
+ if (callback != null) {
+ callback.onCardDetected("MAGNETIC", info);
+ }
+ }
+
+ @Override
+ public void findICCard(String atr) throws RemoteException {
+ MyApplication.app.basicOptV2.buzzerOnDevice(1, 2750, 200, 0);
+ isProcessing = false;
+ if (callback != null) {
+ callback.onEMVCardDetected(AidlConstantsV2.CardType.IC.getValue());
+ }
+ }
+
+ @Override
+ public void findRFCard(String uuid) throws RemoteException {
+ isProcessing = false;
+ if (callback != null) {
+ callback.onEMVCardDetected(AidlConstantsV2.CardType.NFC.getValue());
+ }
+ }
+
+ @Override
+ public void onError(int code, String message) throws RemoteException {
+ isProcessing = false;
+ if (callback != null) {
+ callback.onScanError("EMV Error: " + message);
+ }
+ }
+
+ @Override
+ public void findICCardEx(Bundle info) throws RemoteException {
+ isProcessing = false;
+ if (callback != null) {
+ callback.onEMVCardDetected(AidlConstantsV2.CardType.IC.getValue());
+ }
+ }
+
+ @Override
+ public void findRFCardEx(Bundle info) throws RemoteException {
+ isProcessing = false;
+ if (callback != null) {
+ callback.onEMVCardDetected(AidlConstantsV2.CardType.NFC.getValue());
+ }
+ }
+
+ @Override
+ public void onErrorEx(Bundle info) throws RemoteException {
+ isProcessing = false;
+ String msg = info.getString("message", "Unknown error");
+ if (callback != null) {
+ callback.onScanError("EMV Error: " + msg);
+ }
+ }
+ };
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bdkipoc/transaction/managers/EMVManager.java b/app/src/main/java/com/example/bdkipoc/transaction/managers/EMVManager.java
new file mode 100644
index 0000000..9e07462
--- /dev/null
+++ b/app/src/main/java/com/example/bdkipoc/transaction/managers/EMVManager.java
@@ -0,0 +1,417 @@
+package com.example.bdkipoc.transaction.managers;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.example.bdkipoc.MyApplication;
+import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2;
+import com.sunmi.pay.hardware.aidlv2.bean.EMVCandidateV2;
+import com.sunmi.pay.hardware.aidlv2.emv.EMVListenerV2;
+import com.sunmi.pay.hardware.aidlv2.emv.EMVOptV2;
+
+import java.util.List;
+
+/**
+ * EMVManager - Handles all EMV related operations
+ */
+public class EMVManager {
+ private static final String TAG = "EMVManager";
+
+ private EMVOptV2 mEMVOptV2;
+ private EMVManagerCallback callback;
+
+ // EMV Process Variables
+ private int mCardType;
+ private String mCardNo;
+ private int mPinType;
+ private String mCertInfo;
+ private int mProcessStep;
+
+ public interface EMVManagerCallback {
+ void onAppSelect(String[] candidateNames);
+ void onFinalAppSelect();
+ void onConfirmCardNo(String cardNo);
+ void onCertVerify(String certInfo);
+ void onShowPinPad(int pinType);
+ void onOnlineProcess();
+ void onSignature();
+ void onTransactionSuccess(int code, String desc);
+ void onTransactionFailed(int code, String desc);
+ }
+
+ public EMVManager(EMVManagerCallback callback) {
+ this.callback = callback;
+ initEMVComponents();
+ }
+
+ private void initEMVComponents() {
+ if (MyApplication.app != null) {
+ mEMVOptV2 = MyApplication.app.emvOptV2;
+ Log.d(TAG, "EMV components initialized");
+ } else {
+ Log.e(TAG, "MyApplication.app is null");
+ }
+ }
+
+ public void initEMVData() {
+ try {
+ if (mEMVOptV2 != null) {
+ mEMVOptV2.initEmvProcess();
+
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ try {
+ initEmvTlvData();
+ Log.d(TAG, "EMV data initialized successfully");
+ } catch (Exception e) {
+ Log.e(TAG, "Error in delayed EMV init: " + e.getMessage());
+ }
+ }, 500);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error initializing EMV data: " + e.getMessage());
+ }
+ }
+
+ private void initEmvTlvData() {
+ try {
+ // Set PayPass (MasterCard) TLV data
+ String[] tagsPayPass = {"DF8117", "DF8118", "DF8119", "DF811F", "DF811E", "DF812C",
+ "DF8123", "DF8124", "DF8125", "DF8126", "DF811B", "DF811D", "DF8122", "DF8120", "DF8121"};
+ String[] valuesPayPass = {"E0", "F8", "F8", "E8", "00", "00",
+ "000000000000", "000000100000", "999999999999", "000000100000",
+ "30", "02", "0000000000", "000000000000", "000000000000"};
+ mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYPASS, tagsPayPass, valuesPayPass);
+
+ // Set AMEX TLV data
+ String[] tagsAE = {"9F6D", "9F6E", "9F33", "9F35", "DF8168", "DF8167", "DF8169", "DF8170"};
+ String[] valuesAE = {"C0", "D8E00000", "E0E888", "22", "00", "00", "00", "60"};
+ mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_AE, tagsAE, valuesAE);
+
+ // Set JCB TLV data
+ String[] tagsJCB = {"9F53", "DF8161"};
+ String[] valuesJCB = {"708000", "7F00"};
+ mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_JCB, tagsJCB, valuesJCB);
+
+ // Set DPAS TLV data
+ String[] tagsDPAS = {"9F66"};
+ String[] valuesDPAS = {"B600C000"};
+ mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_DPAS, tagsDPAS, valuesDPAS);
+
+ // Set Flash TLV data
+ String[] tagsFLASH = {"9F58", "9F59", "9F5A", "9F5D", "9F5E"};
+ String[] valuesFLASH = {"03", "D88700", "00", "000000000000", "E000"};
+ mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_FLASH, tagsFLASH, valuesFLASH);
+
+ // Set Pure TLV data
+ String[] tagsPURE = {"DF7F", "DF8134", "DF8133"};
+ String[] valuesPURE = {"A0000007271010", "DF", "36006043F9"};
+ mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PURE, tagsPURE, valuesPURE);
+
+ Bundle bundle = new Bundle();
+ bundle.putBoolean("optOnlineRes", true);
+ mEMVOptV2.setTermParamEx(bundle);
+
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error setting EMV TLV data: " + e.getMessage());
+ }
+ }
+
+ public void startEMVTransaction(String transactionAmount, int cardType) {
+ if (mProcessStep != 0) {
+ Log.d(TAG, "EMV transaction already in progress (step: " + mProcessStep + ") - ignoring call");
+ return;
+ }
+
+ Log.d(TAG, "Starting EMV transaction process");
+ mProcessStep = 1;
+ mCardType = cardType;
+
+ try {
+ mEMVOptV2.initEmvProcess();
+
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ try {
+ if (mProcessStep <= 0) {
+ Log.d(TAG, "EMV process was cancelled - not starting");
+ return;
+ }
+
+ Bundle bundle = new Bundle();
+ bundle.putString("amount", transactionAmount);
+ bundle.putString("transType", "00");
+ bundle.putInt("flowType", AidlConstantsV2.EMV.FlowType.TYPE_EMV_STANDARD);
+ bundle.putInt("cardType", mCardType);
+
+ Log.d(TAG, "Starting transactProcessEx with reset EMV");
+ mEMVOptV2.transactProcessEx(bundle, mEMVListener);
+
+ } catch (Exception e) {
+ Log.e(TAG, "Error in delayed EMV start: " + e.getMessage(), e);
+ if (callback != null) {
+ callback.onTransactionFailed(-1, "Error starting EMV: " + e.getMessage());
+ }
+ }
+ }, 300);
+
+ } catch (Exception e) {
+ Log.e(TAG, "Error starting EMV transaction: " + e.getMessage());
+ if (callback != null) {
+ callback.onTransactionFailed(-1, "Error starting EMV transaction: " + e.getMessage());
+ }
+ }
+ }
+
+ public void resetEMVProcess() {
+ try {
+ if (mEMVOptV2 != null) {
+ mEMVOptV2.initEmvProcess();
+ }
+ mProcessStep = 0;
+ mCardNo = null;
+ Log.d(TAG, "EMV process reset");
+ } catch (Exception e) {
+ Log.e(TAG, "Error resetting EMV process: " + e.getMessage());
+ }
+ }
+
+ // EMV Import Methods
+ public void importAppSelect(int selectIndex) {
+ try {
+ mEMVOptV2.importAppSelect(selectIndex);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void importFinalAppSelectStatus(int status) {
+ try {
+ mEMVOptV2.importAppFinalSelectStatus(status);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void importCardNoStatus(int status) {
+ try {
+ mEMVOptV2.importCardNoStatus(status);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void importCertStatus(int status) {
+ try {
+ mEMVOptV2.importCertStatus(status);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void importPinInputStatus(int inputResult) {
+ try {
+ mEMVOptV2.importPinInputStatus(mPinType, inputResult);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void importSignatureStatus(int status) {
+ try {
+ mEMVOptV2.importSignatureStatus(status);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void mockOnlineProcess() {
+ new Thread(() -> {
+ try {
+ Thread.sleep(2000);
+
+ try {
+ String[] tags = {"71", "72", "91", "8A", "89"};
+ String[] values = {"", "", "", "", ""};
+ byte[] out = new byte[1024];
+ int len = mEMVOptV2.importOnlineProcStatus(0, tags, values, out);
+ if (len < 0) {
+ Log.e(TAG, "importOnlineProcessStatus error,code:" + len);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }).start();
+ }
+
+ // Getters
+ public String getCardNo() {
+ return mCardNo;
+ }
+
+ public int getCardType() {
+ return mCardType;
+ }
+
+ public int getPinType() {
+ return mPinType;
+ }
+
+ // Helper Methods
+ public String maskCardNumber(String cardNo) {
+ if (cardNo == null || cardNo.length() < 8) {
+ return cardNo;
+ }
+ String first4 = cardNo.substring(0, 4);
+ String last4 = cardNo.substring(cardNo.length() - 4);
+ StringBuilder middle = new StringBuilder();
+ for (int i = 0; i < cardNo.length() - 8; i++) {
+ middle.append("*");
+ }
+ return first4 + middle.toString() + last4;
+ }
+
+ public String[] getCandidateNames(List candiList) {
+ if (candiList == null || candiList.size() == 0) return new String[0];
+ String[] result = new String[candiList.size()];
+ for (int i = 0; i < candiList.size(); i++) {
+ EMVCandidateV2 candi = candiList.get(i);
+ String name = candi.appPreName;
+ name = TextUtils.isEmpty(name) ? candi.appLabel : name;
+ name = TextUtils.isEmpty(name) ? candi.appName : name;
+ name = TextUtils.isEmpty(name) ? "" : name;
+ result[i] = name;
+ }
+ return result;
+ }
+
+ // EMV Listener
+ private final EMVListenerV2 mEMVListener = new EMVListenerV2.Stub() {
+
+ @Override
+ public void onWaitAppSelect(List appNameList, boolean isFirstSelect) throws RemoteException {
+ Log.d(TAG, "onWaitAppSelect isFirstSelect:" + isFirstSelect);
+ mProcessStep = 1; // EMV_APP_SELECT
+ String[] candidateNames = getCandidateNames(appNameList);
+ if (callback != null) {
+ callback.onAppSelect(candidateNames);
+ }
+ }
+
+ @Override
+ public void onAppFinalSelect(String tag9F06Value) throws RemoteException {
+ Log.d(TAG, "onAppFinalSelect tag9F06Value:" + tag9F06Value);
+ mProcessStep = 2; // EMV_FINAL_APP_SELECT
+ if (callback != null) {
+ callback.onFinalAppSelect();
+ }
+ }
+
+ @Override
+ public void onConfirmCardNo(String cardNo) throws RemoteException {
+ Log.d(TAG, "onConfirmCardNo cardNo:" + maskCardNumber(cardNo));
+ mCardNo = cardNo;
+ mProcessStep = 3; // EMV_CONFIRM_CARD_NO
+ if (callback != null) {
+ callback.onConfirmCardNo(cardNo);
+ }
+ }
+
+ @Override
+ public void onRequestShowPinPad(int pinType, int remainTime) throws RemoteException {
+ Log.d(TAG, "onRequestShowPinPad pinType:" + pinType + " remainTime:" + remainTime);
+ mPinType = pinType;
+ mProcessStep = 5; // EMV_SHOW_PIN_PAD
+ if (callback != null) {
+ callback.onShowPinPad(pinType);
+ }
+ }
+
+ @Override
+ public void onRequestSignature() throws RemoteException {
+ Log.d(TAG, "onRequestSignature");
+ mProcessStep = 7; // EMV_SIGNATURE
+ if (callback != null) {
+ callback.onSignature();
+ }
+ }
+
+ @Override
+ public void onCertVerify(int certType, String certInfo) throws RemoteException {
+ Log.d(TAG, "onCertVerify certType:" + certType + " certInfo:" + certInfo);
+ mCertInfo = certInfo;
+ mProcessStep = 4; // EMV_CERT_VERIFY
+ if (callback != null) {
+ callback.onCertVerify(certInfo);
+ }
+ }
+
+ @Override
+ public void onOnlineProc() throws RemoteException {
+ Log.d(TAG, "onOnlineProcess");
+ mProcessStep = 6; // EMV_ONLINE_PROCESS
+ if (callback != null) {
+ callback.onOnlineProcess();
+ }
+ }
+
+ @Override
+ public void onCardDataExchangeComplete() throws RemoteException {
+ Log.d(TAG, "onCardDataExchangeComplete");
+ if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) {
+ MyApplication.app.basicOptV2.buzzerOnDevice(1, 2750, 200, 0);
+ }
+ }
+
+ @Override
+ public void onTransResult(int code, String desc) throws RemoteException {
+ Log.d(TAG, "onTransResult code:" + code + " desc:" + desc);
+
+ if (code == 1 || code == 2 || code == 5 || code == 6) {
+ if (callback != null) {
+ callback.onTransactionSuccess(code, desc);
+ }
+ } else {
+ if (callback != null) {
+ callback.onTransactionFailed(code, desc);
+ }
+ }
+ }
+
+ @Override
+ public void onConfirmationCodeVerified() throws RemoteException {
+ Log.d(TAG, "onConfirmationCodeVerified");
+ }
+
+ @Override
+ public void onRequestDataExchange(String cardNo) throws RemoteException {
+ Log.d(TAG, "onRequestDataExchange,cardNo:" + cardNo);
+ mEMVOptV2.importDataExchangeStatus(0);
+ }
+
+ @Override
+ public void onTermRiskManagement() throws RemoteException {
+ Log.d(TAG, "onTermRiskManagement");
+ mEMVOptV2.importTermRiskManagementStatus(0);
+ }
+
+ @Override
+ public void onPreFirstGenAC() throws RemoteException {
+ Log.d(TAG, "onPreFirstGenAC");
+ mEMVOptV2.importPreFirstGenACStatus(0);
+ }
+
+ @Override
+ public void onDataStorageProc(String[] containerID, String[] containerContent) throws RemoteException {
+ Log.d(TAG, "onDataStorageProc");
+ String[] tags = new String[0];
+ String[] values = new String[0];
+ mEMVOptV2.importDataStorage(tags, values);
+ }
+ };
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bdkipoc/transaction/managers/ModalManager.java b/app/src/main/java/com/example/bdkipoc/transaction/managers/ModalManager.java
new file mode 100644
index 0000000..262cb27
--- /dev/null
+++ b/app/src/main/java/com/example/bdkipoc/transaction/managers/ModalManager.java
@@ -0,0 +1,121 @@
+package com.example.bdkipoc.transaction.managers;
+
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.util.Log;
+
+import com.example.bdkipoc.R;
+
+/**
+ * ModalManager - Handles modal UI operations
+ */
+public class ModalManager {
+ private static final String TAG = "ModalManager";
+
+ private FrameLayout modalOverlay;
+ private TextView modalText;
+ private ImageView modalIcon;
+ private Animation fadeIn;
+ private Animation fadeOut;
+ private boolean isModalShowing = false;
+
+ public ModalManager(FrameLayout modalOverlay, TextView modalText, ImageView modalIcon) {
+ this.modalOverlay = modalOverlay;
+ this.modalText = modalText;
+ this.modalIcon = modalIcon;
+ initAnimations();
+ }
+
+ private void initAnimations() {
+ fadeIn = AnimationUtils.loadAnimation(modalOverlay.getContext(), android.R.anim.fade_in);
+ fadeOut = AnimationUtils.loadAnimation(modalOverlay.getContext(), android.R.anim.fade_out);
+
+ fadeIn.setDuration(300);
+ fadeOut.setDuration(300);
+ }
+
+ public void showScanCardModal() {
+ if (isModalShowing) return;
+
+ modalOverlay.post(() -> {
+ modalText.setText("Silakan Tempelkan / Gesekkan / Masukkan Kartu ke Perangkat");
+ modalIcon.setImageResource(R.drawable.ic_card_insert);
+
+ modalOverlay.setVisibility(View.VISIBLE);
+ modalOverlay.startAnimation(fadeIn);
+
+ isModalShowing = true;
+ Log.d(TAG, "Modal scan card shown");
+ });
+ }
+
+ public void showProcessingModal(String message) {
+ if (!isModalShowing) {
+ modalOverlay.post(() -> {
+ modalText.setText(message);
+ modalIcon.setImageResource(R.drawable.ic_card_insert);
+
+ modalOverlay.setVisibility(View.VISIBLE);
+ modalOverlay.startAnimation(fadeIn);
+
+ isModalShowing = true;
+ Log.d(TAG, "Modal processing shown: " + message);
+ });
+ } else {
+ // Just update text if modal already showing
+ modalOverlay.post(() -> {
+ modalText.setText(message);
+ Log.d(TAG, "Modal text updated: " + message);
+ });
+ }
+ }
+
+ public void hideModal() {
+ if (!isModalShowing) return;
+
+ modalOverlay.post(() -> {
+ fadeOut.setAnimationListener(new Animation.AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {}
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ modalOverlay.setVisibility(View.GONE);
+ isModalShowing = false;
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {}
+ });
+
+ modalOverlay.startAnimation(fadeOut);
+ Log.d(TAG, "Modal hidden");
+ });
+ }
+
+ public boolean isShowing() {
+ return isModalShowing;
+ }
+
+ public void updateText(String text) {
+ if (isModalShowing) {
+ modalOverlay.post(() -> {
+ modalText.setText(text);
+ Log.d(TAG, "Modal text updated: " + text);
+ });
+ }
+ }
+
+ public void updateIcon(int iconResource) {
+ if (isModalShowing) {
+ modalOverlay.post(() -> {
+ modalIcon.setImageResource(iconResource);
+ Log.d(TAG, "Modal icon updated");
+ });
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/example/bdkipoc/transaction/managers/PinPadManager.java b/app/src/main/java/com/example/bdkipoc/transaction/managers/PinPadManager.java
new file mode 100644
index 0000000..c31d10f
--- /dev/null
+++ b/app/src/main/java/com/example/bdkipoc/transaction/managers/PinPadManager.java
@@ -0,0 +1,142 @@
+package com.example.bdkipoc.transaction.managers;
+
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.example.bdkipoc.MyApplication;
+import com.example.bdkipoc.utils.ByteUtil;
+import com.sunmi.pay.hardware.aidlv2.AidlErrorCodeV2;
+import com.sunmi.pay.hardware.aidlv2.bean.PinPadConfigV2;
+import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadListenerV2;
+import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadOptV2;
+
+/**
+ * PinPadManager - Handles PIN pad operations
+ */
+public class PinPadManager {
+ private static final String TAG = "PinPadManager";
+
+ private PinPadOptV2 mPinPadOptV2;
+ private PinPadManagerCallback callback;
+
+ public interface PinPadManagerCallback {
+ void onPinInputLength(int length);
+ void onPinInputConfirmed(byte[] pinBlock);
+ void onPinInputCancelled();
+ void onPinInputError(int code, String message);
+ }
+
+ public PinPadManager(PinPadManagerCallback callback) {
+ this.callback = callback;
+ initPinPadComponents();
+ }
+
+ private void initPinPadComponents() {
+ if (MyApplication.app != null) {
+ mPinPadOptV2 = MyApplication.app.pinPadOptV2;
+ Log.d(TAG, "PIN Pad components initialized");
+ } else {
+ Log.e(TAG, "MyApplication.app is null");
+ }
+ }
+
+ public void initPinPad(String cardNo, int pinType) {
+ Log.d(TAG, "========== PIN PAD INITIALIZATION ==========");
+ try {
+ if (mPinPadOptV2 == null) {
+ throw new IllegalStateException("PIN Pad service not available");
+ }
+
+ if (cardNo == null || cardNo.length() < 13) {
+ throw new IllegalArgumentException("Invalid card number for PIN");
+ }
+
+ PinPadConfigV2 pinPadConfig = new PinPadConfigV2();
+ pinPadConfig.setPinPadType(0);
+ pinPadConfig.setPinType(pinType);
+ pinPadConfig.setOrderNumKey(true); // Set to true for normal order, false for random
+
+ String panForPin = cardNo.substring(cardNo.length() - 13, cardNo.length() - 1);
+ byte[] panBytes = panForPin.getBytes("US-ASCII");
+ pinPadConfig.setPan(panBytes);
+
+ pinPadConfig.setTimeout(60 * 1000);
+ pinPadConfig.setPinKeyIndex(12);
+ pinPadConfig.setMaxInput(12);
+ pinPadConfig.setMinInput(0);
+ pinPadConfig.setKeySystem(0);
+ pinPadConfig.setAlgorithmType(0);
+
+ Log.d(TAG, "Initializing PIN pad with config");
+ mPinPadOptV2.initPinPad(pinPadConfig, mPinPadListener);
+
+ } catch (Exception e) {
+ Log.e(TAG, "PIN pad initialization failed: " + e.getMessage());
+ if (callback != null) {
+ callback.onPinInputError(-1, "PIN Error: " + e.getMessage());
+ }
+ }
+ }
+
+ public void cancelPinInput() {
+ try {
+ if (mPinPadOptV2 != null) {
+ // Cancel PIN input if needed
+ Log.d(TAG, "PIN input cancelled");
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Error cancelling PIN input: " + e.getMessage());
+ }
+ }
+
+ // PIN Pad Listener
+ private final PinPadListenerV2 mPinPadListener = new PinPadListenerV2.Stub() {
+ @Override
+ public void onPinLength(int len) throws RemoteException {
+ Log.d(TAG, "PIN input length: " + len);
+ if (callback != null) {
+ callback.onPinInputLength(len);
+ }
+ }
+
+ @Override
+ public void onConfirm(int i, byte[] pinBlock) throws RemoteException {
+ Log.d(TAG, "PIN input confirmed");
+
+ if (pinBlock != null) {
+ String hexStr = ByteUtil.bytes2HexStr(pinBlock);
+ Log.d(TAG, "PIN block received: " + hexStr);
+ if (callback != null) {
+ callback.onPinInputConfirmed(pinBlock);
+ }
+ } else {
+ Log.d(TAG, "PIN bypass confirmed");
+ if (callback != null) {
+ callback.onPinInputConfirmed(null);
+ }
+ }
+ }
+
+ @Override
+ public void onCancel() throws RemoteException {
+ Log.d(TAG, "PIN input cancelled by user");
+ if (callback != null) {
+ callback.onPinInputCancelled();
+ }
+ }
+
+ @Override
+ public void onError(int code) throws RemoteException {
+ Log.e(TAG, "PIN pad error: " + code);
+ String msg = AidlErrorCodeV2.valueOf(code).getMsg();
+ if (callback != null) {
+ callback.onPinInputError(code, msg);
+ }
+ }
+
+ @Override
+ public void onHover(int event, byte[] data) throws RemoteException {
+ Log.d(TAG, "PIN pad hover event: " + event);
+ }
+ };
+}
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_create_transaction.xml b/app/src/main/res/layout/activity_create_transaction.xml
index ec0b3b2..5f20624 100644
--- a/app/src/main/res/layout/activity_create_transaction.xml
+++ b/app/src/main/res/layout/activity_create_transaction.xml
@@ -1,203 +1,276 @@
-
-
+
+ android:layout_height="match_parent"
+ android:fillViewport="true">
-
-
-
-
+ android:orientation="vertical">
-
-
+
+
+
+
+ android:orientation="vertical"
+ android:padding="16dp">
-
+
+ android:layout_marginBottom="24dp"
+ app:cardCornerRadius="12dp"
+ app:cardElevation="4dp">
-
+ android:orientation="vertical"
+ android:padding="20dp">
-
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
+ android:layout_marginBottom="24dp"
+ app:cardCornerRadius="12dp"
+ app:cardElevation="4dp">
-
+ android:orientation="vertical"
+ android:padding="16dp">
-
-
+
-
-
+
+
-
+
+
-
+
-
-
+
-
+
+
-
+
-
-
+
-
+
+
-
+
-
-
+
-
+
+
-
+
-
-
-
+
-
-
+
+
+
-
-
+
+
+
+
+
+
-
-
\ No newline at end of file
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_emv_transaction.xml b/app/src/main/res/layout/activity_emv_transaction.xml
deleted file mode 100644
index b23ea86..0000000
--- a/app/src/main/res/layout/activity_emv_transaction.xml
+++ /dev/null
@@ -1,160 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file