From f4e5e030779bacc04f6d00839847d4d496eb2951 Mon Sep 17 00:00:00 2001 From: riz081 Date: Sat, 21 Jun 2025 01:15:40 +0700 Subject: [PATCH] adding input amount transaction --- .../bdkipoc/kredit/CreditCardActivity.java | 685 ++++++++++-------- .../main/res/layout/activity_credit_card.xml | 353 ++++++++- 2 files changed, 704 insertions(+), 334 deletions(-) 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 d91f3bf..ba3c26c 100644 --- a/app/src/main/java/com/example/bdkipoc/kredit/CreditCardActivity.java +++ b/app/src/main/java/com/example/bdkipoc/kredit/CreditCardActivity.java @@ -12,6 +12,8 @@ import android.text.TextUtils; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; +import android.widget.EditText; +import android.view.View; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; @@ -42,22 +44,39 @@ import java.util.Set; import java.util.TreeMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.text.NumberFormat; +import java.util.Locale; /** - * Enhanced CreditCardActivity with EMV capabilities - * This activity can detect cards and read complete EMV data including PAN, card type, etc. + * Enhanced CreditCardActivity with EMV capabilities and Amount Input */ public class CreditCardActivity extends AppCompatActivity { private static final String TAG = "CreditCard"; + // UI State Constants + private static final int STATE_INPUT_AMOUNT = 0; + private static final int STATE_SCANNING = 1; + private static final int STATE_EMV_PROCESS = 2; + // UI Components private TextView tvResult; private TextView tvModeIndicator; + private TextView tvAmountDisplay; + private EditText etAmountInput; private Button btnCheckCard; private Button btnToggleMode; private Button btnCopyData; private Button btnClearData; - private boolean checkingCard; + + // Amount Input Keypad + private Button btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn0, btn00, btnClear; + private View layoutAmountInput; + private View layoutCardResult; + + // State Management + private int currentState = STATE_INPUT_AMOUNT; + private boolean checkingCard = false; + private String transactionAmount = "0"; // EMV Components private EMVOptV2 mEMVOptV2; @@ -71,7 +90,7 @@ public class CreditCardActivity extends AppCompatActivity { private int mSelectIndex; private int mProcessStep; private AlertDialog mAppSelectDialog; - private boolean isEMVMode = false; // Flag to control EMV vs simple card detection + private boolean isEMVMode = false; // EMV Process Constants private static final int EMV_APP_SELECT = 1; @@ -105,12 +124,10 @@ public class CreditCardActivity extends AppCompatActivity { break; case EMV_CONFIRM_CARD_NO: - // ✅ Tampilkan UI konfirmasi, JANGAN langsung import showCardNumberConfirmation(); break; case EMV_CERT_VERIFY: - // ✅ Tampilkan UI sertifikat, JANGAN langsung import showCertificateVerification(); break; @@ -150,13 +167,12 @@ public class CreditCardActivity extends AppCompatActivity { break; case EMV_TRANS_FAIL: - resetUI(); + resetToAmountInput(); showToast("Transaction failed: " + msg.obj + " -- " + msg.arg1); break; case EMV_TRANS_SUCCESS: - resetUI(); - getTlvData(); // Get complete card data + getTlvData(); // Get complete card data with amount showToast("Transaction successful!"); break; } @@ -173,6 +189,7 @@ public class CreditCardActivity extends AppCompatActivity { initView(); initEMVData(); checkPaySDKStatus(); + updateUIForCurrentState(); } private void initEMVComponents() { @@ -188,12 +205,8 @@ public class CreditCardActivity extends AppCompatActivity { private void initEMVData() { try { if (mEMVOptV2 != null) { - // Initialize EMV process mEMVOptV2.initEmvProcess(); - - // Set EMV TLV data for different card schemes initEmvTlvData(); - android.util.Log.d(TAG, "EMV data initialized successfully"); } } catch (Exception e) { @@ -271,193 +284,247 @@ public class CreditCardActivity extends AppCompatActivity { } } - // Initialize required UI components + // Initialize UI components tvResult = findViewById(R.id.tv_result); - btnCheckCard = findViewById(R.id.btn_check_card); - - // Initialize optional UI components (may not exist in current layout) tvModeIndicator = findViewById(R.id.tv_mode_indicator); + tvAmountDisplay = findViewById(R.id.tv_amount_display); + etAmountInput = findViewById(R.id.et_amount_input); + btnCheckCard = findViewById(R.id.btn_check_card); btnToggleMode = findViewById(R.id.btn_toggle_mode); btnCopyData = findViewById(R.id.btn_copy_data); btnClearData = findViewById(R.id.btn_clear_data); - // Setup button listeners + // Initialize layouts + layoutAmountInput = findViewById(R.id.layout_amount_input); + layoutCardResult = findViewById(R.id.layout_card_result); + + // 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); + + setupKeypadListeners(); + setupButtonListeners(); + + // Initialize amount display + updateAmountDisplay(); + } + + private void setupKeypadListeners() { + 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); + + btnClear.setOnClickListener(v -> clearAmount()); + } + + private void setupButtonListeners() { if (btnToggleMode != null) { btnToggleMode.setOnClickListener(v -> toggleEMVMode()); - } else { - android.util.Log.w(TAG, "btnToggleMode not found in layout"); } if (btnCheckCard != null) { - android.util.Log.d(TAG, "Button found, setting click listener"); - btnCheckCard.setOnClickListener(v -> { - android.util.Log.d(TAG, "Button clicked!"); - switchCheckCard(); - }); - - // Add long click listener as alternative to toggle EMV mode (if toggle button doesn't exist) - if (btnToggleMode == null) { - btnCheckCard.setOnLongClickListener(v -> { - toggleEMVMode(); - return true; - }); - android.util.Log.d(TAG, "Long press listener added for EMV toggle"); - } - } else { - android.util.Log.e(TAG, "Button not found!"); + btnCheckCard.setOnClickListener(v -> handleMainButtonClick()); } if (btnCopyData != null) { btnCopyData.setOnClickListener(v -> copyCardDataToClipboard()); - } else { - android.util.Log.w(TAG, "btnCopyData not found in layout"); } if (btnClearData != null) { btnClearData.setOnClickListener(v -> clearCardData()); + } + } + + private void appendToAmount(String number) { + if (currentState != STATE_INPUT_AMOUNT) return; + + // 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 (currentState != STATE_INPUT_AMOUNT) return; + + if (transactionAmount.length() > 1) { + transactionAmount = transactionAmount.substring(0, transactionAmount.length() - 1); } else { - android.util.Log.w(TAG, "btnClearData not found in layout"); + 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); } - if (tvResult != null) { - updateModeDisplay(); - android.util.Log.d(TAG, "TextView initialized"); - } else { - android.util.Log.e(TAG, "TextView not found!"); + if (etAmountInput != null) { + etAmountInput.setText(transactionAmount); } } + private void handleMainButtonClick() { + switch (currentState) { + case STATE_INPUT_AMOUNT: + handleConfirmAmount(); + break; + case STATE_SCANNING: + handleScanAction(); + break; + case STATE_EMV_PROCESS: + handleEMVAction(); + break; + } + } + + private void handleConfirmAmount() { + long amountCents = Long.parseLong(transactionAmount); + if (amountCents < 100) { // Minimum Rp 1.00 + showToast("Minimum amount is Rp 1.00"); + return; + } + + // Transition to scanning state + currentState = STATE_SCANNING; + updateUIForCurrentState(); + + android.util.Log.d(TAG, "Amount confirmed: " + transactionAmount + " cents"); + showToast("Amount confirmed: " + formatAmount(amountCents)); + } + + private void handleScanAction() { + if (mProcessStep == 0) { + if (checkingCard) { + stopCardCheck(); + } else { + startCardCheck(); + } + } else if (mProcessStep == EMV_CONFIRM_CARD_NO) { + android.util.Log.d(TAG, "User confirmed card number"); + showLoadingDialog("Processing card confirmation..."); + btnCheckCard.setText("Processing..."); + importCardNoStatus(0); + } else if (mProcessStep == EMV_CERT_VERIFY) { + android.util.Log.d(TAG, "User confirmed certificate"); + showLoadingDialog("Processing certificate..."); + btnCheckCard.setText("Processing..."); + importCertStatus(0); + } + } + + private void handleEMVAction() { + // Handle EMV process actions (same as before) + android.util.Log.d(TAG, "EMV action in process step: " + mProcessStep); + } + + private void updateUIForCurrentState() { + runOnUiThread(() -> { + switch (currentState) { + case STATE_INPUT_AMOUNT: + showAmountInput(); + break; + case STATE_SCANNING: + showScanningUI(); + break; + case STATE_EMV_PROCESS: + // UI handled by EMV process + break; + } + }); + } + + private void showAmountInput() { + layoutAmountInput.setVisibility(View.VISIBLE); + layoutCardResult.setVisibility(View.GONE); + + btnCheckCard.setText("Konfirmasi"); + btnCheckCard.setEnabled(true); + + if (tvResult != null) { + tvResult.setText("Masukkan nominal pembayaran\nGunakan keypad di bawah untuk memasukkan nominal"); + } + + updateModeDisplay(); + updateAmountDisplay(); + } + + private void showScanningUI() { + layoutAmountInput.setVisibility(View.GONE); + layoutCardResult.setVisibility(View.VISIBLE); + + btnCheckCard.setText("Start Scanning"); + btnCheckCard.setEnabled(true); + + if (tvResult != null) { + String mode = isEMVMode ? "EMV Mode (Full Card Data)" : "Simple Mode (Basic Detection)"; + tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + + "\n\nReady to scan card...\nCurrent Mode: " + mode + + "\nPlace your card on the reader"); + } + + updateModeDisplay(); + } + private void toggleEMVMode() { isEMVMode = !isEMVMode; updateModeDisplay(); android.util.Log.d(TAG, "EMV Mode toggled to: " + isEMVMode); - // If no toggle button exists, show toast to inform user if (btnToggleMode == null) { showToast("Mode switched to: " + (isEMVMode ? "EMV Mode" : "Simple Mode")); } } - // Alternative method to toggle EMV mode programmatically or via menu - public void switchToEMVMode() { - isEMVMode = true; - updateModeDisplay(); - android.util.Log.d(TAG, "Switched to EMV Mode"); - showToast("Switched to EMV Mode (Full Card Data)"); - } - - public void switchToSimpleMode() { - isEMVMode = false; - updateModeDisplay(); - android.util.Log.d(TAG, "Switched to Simple Mode"); - showToast("Switched to Simple Mode (Basic Detection)"); - } - private void updateModeDisplay() { String mode = isEMVMode ? "EMV Mode (Full Card Data)" : "Simple Mode (Basic Detection)"; if (tvModeIndicator != null) { tvModeIndicator.setText("Current Mode: " + mode); } - - if (tvResult != null) { - tvResult.setText("Ready to scan card...\nCurrent Mode: " + mode + "\nPlace your card on the reader"); - } } - // ====== COPY AND CLEAR FUNCTIONALITY ====== - private void copyCardDataToClipboard() { - if (tvResult != null) { - String cardData = tvResult.getText().toString(); - if (!cardData.isEmpty() && !cardData.contains("Ready to scan")) { - ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText("Card Data", cardData); - clipboard.setPrimaryClip(clip); - showToast("Card data copied to clipboard"); - } else { - showToast("No card data to copy"); - } - } - } - - private void clearCardData() { - updateModeDisplay(); - - // Hide copy and clear buttons (if they exist) - if (btnCopyData != null) { - btnCopyData.setVisibility(android.view.View.GONE); - } - if (btnClearData != null) { - btnClearData.setVisibility(android.view.View.GONE); - } - - showToast("Card data cleared"); - } - - @Override - public boolean onSupportNavigateUp() { - onBackPressed(); - return true; - } - - private void switchCheckCard() { - android.util.Log.d(TAG, "switchCheckCard called, mProcessStep: " + mProcessStep); - - try { - // Handle berbagai process steps - if (mProcessStep == 0) { - // Initial state - start/stop card scanning - if (checkingCard) { - android.util.Log.d(TAG, "Stopping card check"); - stopCardCheck(); - } else { - android.util.Log.d(TAG, "Starting card check"); - startCardCheck(); - } - } else if (mProcessStep == EMV_CONFIRM_CARD_NO) { - // ✅ Handle card number confirmation - android.util.Log.d(TAG, "User confirmed card number"); - showLoadingDialog("Processing card confirmation..."); - btnCheckCard.setText("Processing..."); - - // ✅ Import card number status dan reset process step - importCardNoStatus(0); - - } else if (mProcessStep == EMV_CERT_VERIFY) { - // ✅ Handle certificate verification - android.util.Log.d(TAG, "User confirmed certificate"); - showLoadingDialog("Processing certificate..."); - btnCheckCard.setText("Processing..."); - - // ✅ Import certificate status - importCertStatus(0); - - } else { - android.util.Log.w(TAG, "Unknown process step: " + mProcessStep); - } - - } catch (Exception e) { - android.util.Log.e(TAG, "Error in switchCheckCard: " + e.getMessage()); - e.printStackTrace(); - updateUI("Error: " + e.getMessage(), false); - resetUI(); - } - } - - private void showLoadingDialog(String message) { - runOnUiThread(() -> { - if (tvResult != null) { - tvResult.setText(message + "..."); - } - android.util.Log.d(TAG, "Loading: " + message); - }); - } - - private void dismissLoadingDialog() { - // Method ini bisa kosong atau implement actual dialog dismissal - android.util.Log.d(TAG, "Loading dismissed"); + private String formatAmount(long amountCents) { + double amountRupiah = amountCents / 100.0; + NumberFormat formatter = NumberFormat.getCurrencyInstance(new Locale("id", "ID")); + return formatter.format(amountRupiah); } + // ====== CARD SCANNING METHODS (Same as before) ====== private void startCardCheck() { try { if (isEMVMode) { @@ -467,6 +534,7 @@ public class CreditCardActivity extends AppCompatActivity { } checkingCard = true; btnCheckCard.setText("Stop Scanning"); + currentState = STATE_EMV_PROCESS; } catch (Exception e) { android.util.Log.e(TAG, "Error starting card check: " + e.getMessage()); updateUI("Error starting card check: " + e.getMessage(), false); @@ -474,7 +542,6 @@ public class CreditCardActivity extends AppCompatActivity { } private void startSimpleCardCheck() { - // Original simple card detection logic try { if (!validateApplicationState()) { return; @@ -522,11 +589,11 @@ public class CreditCardActivity extends AppCompatActivity { return; } - // Initialize EMV process before card check mEMVOptV2.initEmvProcess(); initEmvTlvData(); - tvResult.setText("EMV Mode: Starting card scan...\nPlease insert, swipe, or tap your card"); + tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + + "\n\nEMV Mode: Starting card scan...\nPlease insert, swipe, or tap your card"); int cardType = AidlConstantsV2.CardType.NFC.getValue() | AidlConstantsV2.CardType.IC.getValue(); android.util.Log.d(TAG, "Starting EMV checkCard with cardType: " + cardType); @@ -543,7 +610,8 @@ public class CreditCardActivity extends AppCompatActivity { private void startSimpleCardScan() { try { int cardType = CardType.MAGNETIC.getValue() | CardType.IC.getValue() | CardType.NFC.getValue(); - tvResult.setText("Simple Mode: Starting card scan...\nPlease insert, swipe, or tap your card"); + tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + + "\n\nSimple Mode: Starting card scan...\nPlease insert, swipe, or tap your card"); android.util.Log.d(TAG, "Starting simple checkCard with cardType: " + cardType); @@ -563,7 +631,8 @@ public class CreditCardActivity extends AppCompatActivity { } checkingCard = false; btnCheckCard.setText("Start Scanning"); - updateModeDisplay(); + currentState = STATE_SCANNING; + updateUIForCurrentState(); } catch (Exception e) { android.util.Log.e(TAG, "Error stopping card check: " + e.getMessage()); checkingCard = false; @@ -580,7 +649,7 @@ public class CreditCardActivity extends AppCompatActivity { return true; } - // ====== SIMPLE CARD DETECTION CALLBACK ====== + // ====== SIMPLE CARD DETECTION CALLBACK (Same as before) ====== private final CheckCardCallbackV2 mSimpleCheckCardCallback = new CheckCardCallbackV2.Stub() { @Override public void findMagCard(Bundle info) throws RemoteException { @@ -632,7 +701,7 @@ public class CreditCardActivity extends AppCompatActivity { } }; - // ====== EMV CARD DETECTION CALLBACK ====== + // ====== EMV CARD DETECTION CALLBACK (Same as before) ====== private final CheckCardCallbackV2 mEMVCheckCardCallback = new CheckCardCallbackV2.Stub() { @Override public void findMagCard(Bundle info) throws RemoteException { @@ -643,7 +712,6 @@ public class CreditCardActivity extends AppCompatActivity { @Override public void findICCard(String atr) throws RemoteException { android.util.Log.d(TAG, "EMV Mode: findICCard callback triggered with ATR: " + atr); - // IC card detected, start EMV process MyApplication.app.basicOptV2.buzzerOnDevice(1, 2750, 200, 0); mCardType = AidlConstantsV2.CardType.IC.getValue(); runOnUiThread(() -> startEMVTransactionProcess()); @@ -652,7 +720,6 @@ public class CreditCardActivity extends AppCompatActivity { @Override public void findRFCard(String uuid) throws RemoteException { android.util.Log.d(TAG, "EMV Mode: findRFCard callback triggered with UUID: " + uuid); - // NFC card detected, start EMV process mCardType = AidlConstantsV2.CardType.NFC.getValue(); runOnUiThread(() -> startEMVTransactionProcess()); } @@ -687,7 +754,7 @@ public class CreditCardActivity extends AppCompatActivity { } }; - // ====== SIMPLE CARD RESULT HANDLERS ====== + // ====== SIMPLE CARD RESULT HANDLERS (Updated with amount) ====== private void handleSimpleMagCardResult(Bundle info) { logMagneticCardData(info); @@ -695,11 +762,14 @@ public class CreditCardActivity extends AppCompatActivity { String track2 = Utility.null2String(info.getString("TRACK2")); String track3 = Utility.null2String(info.getString("TRACK3")); - StringBuilder sb = new StringBuilder() - .append("MAGNETIC CARD DETECTED (Simple Mode)\n") - .append("Track1: ").append(track1.isEmpty() ? "N/A" : track1).append("\n") - .append("Track2: ").append(track2.isEmpty() ? "N/A" : track2).append("\n") - .append("Track3: ").append(track3.isEmpty() ? "N/A" : track3); + StringBuilder sb = new StringBuilder(); + sb.append("==== TRANSACTION SUMMARY ====\n"); + sb.append("Amount: ").append(formatAmount(Long.parseLong(transactionAmount))).append("\n"); + sb.append("Payment Method: Magnetic Card\n\n"); + sb.append("MAGNETIC CARD DETECTED (Simple Mode)\n"); + sb.append("Track1: ").append(track1.isEmpty() ? "N/A" : track1).append("\n"); + sb.append("Track2: ").append(track2.isEmpty() ? "N/A" : track2).append("\n"); + sb.append("Track3: ").append(track3.isEmpty() ? "N/A" : track3); updateUI(sb.toString(), true); } @@ -711,8 +781,11 @@ public class CreditCardActivity extends AppCompatActivity { int cardType = info.getInt("cardType", -1); StringBuilder sb = new StringBuilder(); - sb.append("IC CARD DETECTED (Simple Mode)\n") - .append("ATR: ").append(atr.isEmpty() ? "N/A" : atr).append("\n"); + sb.append("==== TRANSACTION SUMMARY ====\n"); + sb.append("Amount: ").append(formatAmount(Long.parseLong(transactionAmount))).append("\n"); + sb.append("Payment Method: IC Card\n\n"); + sb.append("IC CARD DETECTED (Simple Mode)\n"); + sb.append("ATR: ").append(atr.isEmpty() ? "N/A" : atr).append("\n"); if (cardType != -1) { sb.append("Card Type: ").append(cardType).append("\n"); } @@ -728,8 +801,11 @@ public class CreditCardActivity extends AppCompatActivity { int sak = info.getInt("sak", -1); StringBuilder sb = new StringBuilder(); - sb.append("RF/NFC CARD DETECTED (Simple Mode)\n") - .append("UUID: ").append(uuid.isEmpty() ? "N/A" : uuid).append("\n"); + sb.append("==== TRANSACTION SUMMARY ====\n"); + sb.append("Amount: ").append(formatAmount(Long.parseLong(transactionAmount))).append("\n"); + sb.append("Payment Method: NFC/RF Card\n\n"); + sb.append("RF/NFC CARD DETECTED (Simple Mode)\n"); + sb.append("UUID: ").append(uuid.isEmpty() ? "N/A" : uuid).append("\n"); if (!ats.isEmpty()) { sb.append("ATS: ").append(ats).append("\n"); } @@ -750,11 +826,9 @@ public class CreditCardActivity extends AppCompatActivity { updateUI(error, true); } - // ====== EMV CARD RESULT HANDLERS ====== + // ====== EMV CARD RESULT HANDLERS (Same as before) ====== private void handleEMVMagCardResult(Bundle info) { - // For magnetic cards in EMV mode, fall back to simple processing handleSimpleMagCardResult(info); - // Add EMV suffix to indicate mode String currentText = tvResult.getText().toString(); tvResult.setText(currentText.replace("(Simple Mode)", "(EMV Mode - Magnetic)")); } @@ -768,12 +842,12 @@ public class CreditCardActivity extends AppCompatActivity { updateUI(error, true); } - // ====== EMV TRANSACTION PROCESS ====== + // ====== EMV TRANSACTION PROCESS (Updated with amount) ====== private void startEMVTransactionProcess() { android.util.Log.d(TAG, "Starting EMV transaction process"); try { Bundle bundle = new Bundle(); - bundle.putString("amount", "100"); // Default amount 1.00 + bundle.putString("amount", transactionAmount); // Use actual transaction amount bundle.putString("transType", "00"); if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) { @@ -783,7 +857,8 @@ public class CreditCardActivity extends AppCompatActivity { } bundle.putInt("cardType", mCardType); - tvResult.setText("EMV processing started...\nReading card data..."); + tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + + "\n\nEMV processing started...\nReading card data..."); mEMVOptV2.transactProcessEx(bundle, mEMVListener); } catch (Exception e) { @@ -793,7 +868,7 @@ public class CreditCardActivity extends AppCompatActivity { } } - // ====== EMV LISTENER ====== + // ====== EMV LISTENER (Same as before) ====== private final EMVListenerV2 mEMVListener = new EMVListenerV2.Stub() { @Override @@ -877,7 +952,6 @@ public class CreditCardActivity extends AppCompatActivity { @Override public void onConfirmationCodeVerified() throws RemoteException { android.util.Log.d(TAG, "onConfirmationCodeVerified"); - // Handle See Phone flow } @Override @@ -907,7 +981,7 @@ public class CreditCardActivity extends AppCompatActivity { } }; - // ====== EMV DIALOG HANDLERS ====== + // ====== EMV DIALOG HANDLERS (Same as before) ====== private void showAppSelectDialog(String[] candidateNames) { mAppSelectDialog = new AlertDialog.Builder(this) .setTitle("Please select application") @@ -921,16 +995,14 @@ public class CreditCardActivity extends AppCompatActivity { private void showCardNumberConfirmation() { runOnUiThread(() -> { - dismissLoadingDialog(); // Pastikan loading dialog ditutup + dismissLoadingDialog(); - String displayText = "Card Number: " + maskCardNumber(mCardNo) + - "\n\nPlease confirm this card number to continue"; + String displayText = "Amount: " + formatAmount(Long.parseLong(transactionAmount)) + + "\n\nCard Number: " + maskCardNumber(mCardNo) + + "\n\nPlease confirm this card number to continue"; tvResult.setText(displayText); - // ✅ HANYA ubah text button, JANGAN ubah listener btnCheckCard.setText("Confirm Card Number"); - - // ✅ Set process step untuk onClick handler mProcessStep = EMV_CONFIRM_CARD_NO; android.util.Log.d(TAG, "Card number confirmation UI updated, waiting for user input"); @@ -941,26 +1013,23 @@ public class CreditCardActivity extends AppCompatActivity { runOnUiThread(() -> { dismissLoadingDialog(); - String displayText = "Certificate Information:\n" + mCertInfo + - "\n\nPlease confirm certificate to continue"; + String displayText = "Amount: " + formatAmount(Long.parseLong(transactionAmount)) + + "\n\nCertificate Information:\n" + mCertInfo + + "\n\nPlease confirm certificate to continue"; tvResult.setText(displayText); - // ✅ HANYA ubah text button btnCheckCard.setText("Confirm Certificate"); - - // ✅ Set process step mProcessStep = EMV_CERT_VERIFY; android.util.Log.d(TAG, "Certificate verification UI updated, waiting for user input"); }); } - // ====== TLV DATA RETRIEVAL ====== + // ====== TLV DATA RETRIEVAL (Updated with transaction summary) ====== private void getTlvData() { android.util.Log.d(TAG, "======== RETRIEVING COMPLETE CARD DATA ========"); try { - // Comprehensive TLV tag list based on EMV specifications String[] standardTagList = { "4F", "50", "57", "5A", "5F20", "5F24", "5F25", "5F28", "5F2A", "5F2D", "5F30", "5F34", "82", "84", "87", "88", "8A", "8C", "8D", "8E", "8F", "90", "91", "92", "93", "94", "95", @@ -975,7 +1044,6 @@ public class CreditCardActivity extends AppCompatActivity { "9F74", "9F75", "9F76", "9F77", "9F78", "9F79", "9F7A", "9F7B", "9F7C", "9F7D", "9F7E", "9F7F" }; - // PayPass specific tags String[] payPassTagList = { "DF810C", "DF8117", "DF8118", "DF8119", "DF811A", "DF811B", "DF811C", "DF811D", "DF811E", "DF811F", "DF8120", "DF8121", "DF8122", "DF8123", "DF8124", "DF8125", "DF8126", "DF8127", "DF8128", "DF8129", @@ -987,7 +1055,6 @@ public class CreditCardActivity extends AppCompatActivity { byte[] outData = new byte[4096]; Map allTlvMap = new TreeMap<>(); - // Determine TLV operation code based on card type int tlvOpCode = AidlConstantsV2.EMV.TLVOpCode.OP_NORMAL; if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) { tlvOpCode = AidlConstantsV2.EMV.TLVOpCode.OP_PAYPASS; @@ -996,7 +1063,6 @@ public class CreditCardActivity extends AppCompatActivity { android.util.Log.d(TAG, "Using TLV OpCode: " + tlvOpCode); android.util.Log.d(TAG, "Requesting " + standardTagList.length + " standard tags"); - // Get standard EMV tags int len = mEMVOptV2.getTlvList(tlvOpCode, standardTagList, outData); if (len > 0) { byte[] bytes = Arrays.copyOf(outData, len); @@ -1008,7 +1074,6 @@ public class CreditCardActivity extends AppCompatActivity { android.util.Log.d(TAG, "Parsed " + tlvMap.size() + " standard TLV tags"); } - // Get PayPass specific tags (if NFC card) if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) { android.util.Log.d(TAG, "Requesting " + payPassTagList.length + " PayPass specific tags"); @@ -1024,49 +1089,77 @@ public class CreditCardActivity extends AppCompatActivity { } } - // Build comprehensive display output StringBuilder sb = new StringBuilder(); - // ====== DISPLAY ALL TLV DATA LIKE DEMO APP ====== - android.util.Log.d(TAG, "======== ALL TLV DATA ========"); + // Transaction Summary + sb.append("==== TRANSACTION COMPLETED ====\n"); + sb.append("Amount: ").append(formatAmount(Long.parseLong(transactionAmount))).append("\n"); + sb.append("Payment Method: EMV ").append(mCardType == AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC").append("\n"); + sb.append("Status: SUCCESS\n\n"); + // Card Summary + sb.append("==== CARD SUMMARY ====\n"); + + TLV panTlv = allTlvMap.get("5A"); + if (panTlv != null && !TextUtils.isEmpty(panTlv.getValue())) { + sb.append("Card Number: ").append(panTlv.getValue()).append("\n"); + } + + TLV labelTlv = allTlvMap.get("50"); + if (labelTlv != null && !TextUtils.isEmpty(labelTlv.getValue())) { + sb.append("App Label: ").append(hexToString(labelTlv.getValue())).append("\n"); + } + + TLV nameTlv = allTlvMap.get("5F20"); + if (nameTlv != null && !TextUtils.isEmpty(nameTlv.getValue())) { + sb.append("Cardholder: ").append(hexToString(nameTlv.getValue()).trim()).append("\n"); + } + + TLV expiryTlv = allTlvMap.get("5F24"); + if (expiryTlv != null && !TextUtils.isEmpty(expiryTlv.getValue())) { + String expiry = expiryTlv.getValue(); + if (expiry.length() == 6) { + sb.append("Expiry: ").append(expiry.substring(2, 4)).append("/").append(expiry.substring(0, 2)).append("\n"); + } + } + + TLV aidTlv = allTlvMap.get("9F06"); + if (aidTlv != null && !TextUtils.isEmpty(aidTlv.getValue())) { + sb.append("Scheme: ").append(identifyPaymentScheme(aidTlv.getValue())).append("\n"); + } + + sb.append("\n==== ALL TLV DATA ====\n"); + + // All TLV Data Set keySet = allTlvMap.keySet(); for (String key : keySet) { TLV tlv = allTlvMap.get(key); String value = tlv != null ? tlv.getValue() : ""; String description = getTlvDescription(key); - // Format like the demo app sb.append(key); if (!description.equals("Unknown")) { sb.append(" (").append(description).append(")"); } sb.append(": "); - // Special formatting for specific tags if (key.equals("5A") && !value.isEmpty()) { - // PAN - show full number sb.append(value); } else if (key.equals("50") && !value.isEmpty()) { - // Application Label - convert hex to string String decodedLabel = hexToString(value); sb.append(value).append(" (").append(decodedLabel).append(")"); } else if (key.equals("5F20") && !value.isEmpty()) { - // Cardholder Name - convert hex to string String decodedName = hexToString(value); sb.append(value).append(" (").append(decodedName.trim()).append(")"); } else if (key.equals("9F06") && !value.isEmpty()) { - // Application Identifier - show scheme String scheme = identifyPaymentScheme(value); sb.append(value).append(" (").append(scheme).append(")"); } else if (key.equals("5F24") && !value.isEmpty()) { - // Expiry Date - format YYMMDD sb.append(value); if (value.length() == 6) { sb.append(" (").append(value.substring(2, 4)).append("/").append(value.substring(0, 2)).append(")"); } } else if (key.equals("9F02") && !value.isEmpty()) { - // Amount - format as currency sb.append(value); try { long amount = Long.parseLong(value, 16); @@ -1080,7 +1173,6 @@ public class CreditCardActivity extends AppCompatActivity { sb.append("\n"); - // Log each TLV android.util.Log.d(TAG, "Tag " + key + " (" + description + "): " + value); } @@ -1089,47 +1181,8 @@ public class CreditCardActivity extends AppCompatActivity { android.util.Log.d(TAG, "Total TLV tags retrieved: " + keySet.size()); android.util.Log.d(TAG, "=================================="); - // Also create a summary view - StringBuilder summary = new StringBuilder(); - summary.append("==== CARD SUMMARY ====\n"); - - // Card Number (show full number) - TLV panTlv = allTlvMap.get("5A"); - if (panTlv != null && !TextUtils.isEmpty(panTlv.getValue())) { - summary.append("Card Number: ").append(panTlv.getValue()).append("\n"); - } - - // Application Label - TLV labelTlv = allTlvMap.get("50"); - if (labelTlv != null && !TextUtils.isEmpty(labelTlv.getValue())) { - summary.append("App Label: ").append(hexToString(labelTlv.getValue())).append("\n"); - } - - // Cardholder Name - TLV nameTlv = allTlvMap.get("5F20"); - if (nameTlv != null && !TextUtils.isEmpty(nameTlv.getValue())) { - summary.append("Cardholder: ").append(hexToString(nameTlv.getValue()).trim()).append("\n"); - } - - // Expiry Date - TLV expiryTlv = allTlvMap.get("5F24"); - if (expiryTlv != null && !TextUtils.isEmpty(expiryTlv.getValue())) { - String expiry = expiryTlv.getValue(); - if (expiry.length() == 6) { - summary.append("Expiry: ").append(expiry.substring(2, 4)).append("/").append(expiry.substring(0, 2)).append("\n"); - } - } - - // Card Scheme - TLV aidTlv = allTlvMap.get("9F06"); - if (aidTlv != null && !TextUtils.isEmpty(aidTlv.getValue())) { - summary.append("Scheme: ").append(identifyPaymentScheme(aidTlv.getValue())).append("\n"); - } - - summary.append("\n").append("==== ALL TLV DATA ====\n").append(sb.toString()); - android.util.Log.d(TAG, "Complete card data retrieved successfully"); - updateUI(summary.toString(), true); + updateUI(sb.toString(), true); } catch (Exception e) { android.util.Log.e(TAG, "Error retrieving TLV data: " + e.getMessage()); @@ -1138,14 +1191,49 @@ public class CreditCardActivity extends AppCompatActivity { } } - // ====== HELPER METHODS ====== - - /** - * Get comprehensive TLV tag description for better logging and display - */ + // ====== RESET AND CLEANUP METHODS ====== + private void resetToAmountInput() { + runOnUiThread(() -> { + currentState = STATE_INPUT_AMOUNT; + mProcessStep = 0; + checkingCard = false; + + dismissAppSelectDialog(); + updateUIForCurrentState(); + + if (btnCopyData != null) { + btnCopyData.setVisibility(View.GONE); + } + if (btnClearData != null) { + btnClearData.setVisibility(View.GONE); + } + + android.util.Log.d(TAG, "UI reset to amount input state"); + }); + } + + private void copyCardDataToClipboard() { + if (tvResult != null) { + String cardData = tvResult.getText().toString(); + if (!cardData.isEmpty() && !cardData.contains("Ready to scan")) { + ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText("Card Data", cardData); + clipboard.setPrimaryClip(clip); + showToast("Card data copied to clipboard"); + } else { + showToast("No card data to copy"); + } + } + } + + private void clearCardData() { + resetToAmountInput(); + showToast("Transaction cleared"); + } + + // ====== HELPER METHODS (All the same as before) ====== private String getTlvDescription(String tag) { switch (tag.toUpperCase()) { - // Standard EMV Tags case "4F": return "Application Identifier"; case "50": return "Application Label"; case "57": return "Track 2 Equivalent Data"; @@ -1462,7 +1550,7 @@ public class CreditCardActivity extends AppCompatActivity { return result; } - // ====== EMV IMPORT METHODS ====== + // ====== EMV IMPORT METHODS (Same as before) ====== private void importAppSelect(int selectIndex) { try { mEMVOptV2.importAppSelect(selectIndex); @@ -1511,16 +1599,14 @@ public class CreditCardActivity extends AppCompatActivity { } } - // ====== PIN PAD METHODS ====== + // ====== PIN PAD METHODS (Same as before) ====== private void initPinPad() { android.util.Log.e(TAG, "========== PIN PAD INITIALIZATION =========="); try { - // Validate components if (mPinPadOptV2 == null) { throw new IllegalStateException("PIN Pad service not available"); } - // Validate card number if (mCardNo == null || mCardNo.length() < 13) { throw new IllegalArgumentException("Invalid card number for PIN"); } @@ -1530,26 +1616,24 @@ public class CreditCardActivity extends AppCompatActivity { pinPadConfig.setPinType(mPinType); pinPadConfig.setOrderNumKey(false); - // Use the correct PAN format (last 12 digits excluding check digit) 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); // Make sure this matches your key index + pinPadConfig.setPinKeyIndex(12); pinPadConfig.setMaxInput(12); - pinPadConfig.setMinInput(0); // Allow bypass - pinPadConfig.setKeySystem(0); // 0 = international, 1 = domestic - pinPadConfig.setAlgorithmType(0); // 0 = 3DES, 1 = SM4 + pinPadConfig.setMinInput(0); + pinPadConfig.setKeySystem(0); + pinPadConfig.setAlgorithmType(0); - // Update UI runOnUiThread(() -> { - updateUI("PIN Input Required\n\nPIN pad is ready\nPlease enter your PIN on the device", false); + updateUI("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + + "\n\nPIN Input Required\n\nPIN pad is ready\nPlease enter your PIN on the device", false); btnCheckCard.setText("PIN Pad Active"); btnCheckCard.setEnabled(false); }); - // Initialize PIN pad mPinPadOptV2.initPinPad(pinPadConfig, mPinPadListener); } catch (Exception e) { @@ -1568,7 +1652,8 @@ public class CreditCardActivity extends AppCompatActivity { for (int i = 0; i < len; i++) { dots += "• "; } - tvResult.setText("PIN Input: " + dots + "\n\nEntering PIN... (" + len + " digits)"); + tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + + "\n\nPIN Input: " + dots + "\n\nEntering PIN... (" + len + " digits)"); }); } @@ -1579,7 +1664,8 @@ public class CreditCardActivity extends AppCompatActivity { runOnUiThread(() -> { btnCheckCard.setEnabled(true); btnCheckCard.setText("Processing..."); - tvResult.setText("PIN confirmed, processing..."); + tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + + "\n\nPIN confirmed, processing..."); }); if (pinBlock != null) { @@ -1625,11 +1711,12 @@ public class CreditCardActivity extends AppCompatActivity { } }; - // ====== MOCK ONLINE PROCESS ====== + // ====== MOCK ONLINE PROCESS (Same as before) ====== private void mockOnlineProcess() { new Thread(() -> { try { - runOnUiThread(() -> tvResult.setText("Processing online authorization...")); + runOnUiThread(() -> tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + + "\n\nProcessing online authorization...")); Thread.sleep(2000); try { @@ -1650,50 +1737,41 @@ public class CreditCardActivity extends AppCompatActivity { } // ====== UI UPDATE METHODS ====== - private void updateUI(String message, boolean isCardDetected) { + private void updateUI(String message, boolean isTransactionComplete) { runOnUiThread(() -> { if (tvResult != null) { tvResult.setText(message); } - if (isCardDetected) { + if (isTransactionComplete) { checkingCard = false; - btnCheckCard.setText("Start Scanning"); - btnCheckCard.setOnClickListener(v -> switchCheckCard()); + btnCheckCard.setText("New Transaction"); + btnCheckCard.setOnClickListener(v -> resetToAmountInput()); + currentState = STATE_INPUT_AMOUNT; // Will be updated when button clicked - // Show copy and clear buttons when card data is available (if buttons exist) if (btnCopyData != null && message.contains("====")) { - btnCopyData.setVisibility(android.view.View.VISIBLE); + btnCopyData.setVisibility(View.VISIBLE); } if (btnClearData != null && message.contains("====")) { - btnClearData.setVisibility(android.view.View.VISIBLE); + btnClearData.setVisibility(View.VISIBLE); } } }); } - private void resetUI() { + private void showLoadingDialog(String message) { runOnUiThread(() -> { - mProcessStep = 0; // ✅ Reset process step ke initial state - btnCheckCard.setText("Start Scanning"); - - // ✅ Reset click listener ke original method - btnCheckCard.setOnClickListener(v -> switchCheckCard()); - - dismissAppSelectDialog(); - updateModeDisplay(); - - // Hide copy and clear buttons (if they exist) - if (btnCopyData != null) { - btnCopyData.setVisibility(android.view.View.GONE); + if (tvResult != null) { + tvResult.setText("Amount: " + formatAmount(Long.parseLong(transactionAmount)) + + "\n\n" + message + "..."); } - if (btnClearData != null) { - btnClearData.setVisibility(android.view.View.GONE); - } - - android.util.Log.d(TAG, "UI reset to initial state"); + android.util.Log.d(TAG, "Loading: " + message); }); } + private void dismissLoadingDialog() { + android.util.Log.d(TAG, "Loading dismissed"); + } + private void dismissAppSelectDialog() { if (mAppSelectDialog != null) { try { @@ -1709,7 +1787,7 @@ public class CreditCardActivity extends AppCompatActivity { runOnUiThread(() -> Toast.makeText(this, message, Toast.LENGTH_SHORT).show()); } - // ====== LOGGING METHODS (kept from original) ====== + // ====== LOGGING METHODS (Same as before) ====== private void logMagneticCardData(Bundle info) { android.util.Log.d(TAG, "======================================="); android.util.Log.d(TAG, " MAGNETIC CARD DETECTED "); @@ -1770,6 +1848,13 @@ public class CreditCardActivity extends AppCompatActivity { android.util.Log.e(TAG, "======================================="); } + // ====== LIFECYCLE METHODS (Same as before) ====== + @Override + public boolean onSupportNavigateUp() { + onBackPressed(); + return true; + } + // ====== LIFECYCLE METHODS ====== @Override protected void onDestroy() { diff --git a/app/src/main/res/layout/activity_credit_card.xml b/app/src/main/res/layout/activity_credit_card.xml index b6ee995..35e202c 100644 --- a/app/src/main/res/layout/activity_credit_card.xml +++ b/app/src/main/res/layout/activity_credit_card.xml @@ -1,18 +1,20 @@ - + + android:padding="16dp" + android:background="#F5F5F5"> + android:background="?attr/colorPrimary" + android:titleTextColor="@android:color/white" />