refactor transaction
This commit is contained in:
parent
0af0e836b1
commit
ece79942c1
@ -73,12 +73,8 @@
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".kredit.CreateTransactionActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".kredit.EmvTransactionActivity"
|
||||
android:exported="false" />
|
||||
android:name=".transaction.CreateTransactionActivity"
|
||||
android:exported="false" />
|
||||
|
||||
<activity
|
||||
android:name=".kredit.CreditCardActivity"
|
||||
|
@ -19,8 +19,7 @@ import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import com.google.android.material.button.MaterialButton;
|
||||
|
||||
import com.example.bdkipoc.kredit.CreateTransactionActivity;
|
||||
import com.example.bdkipoc.kredit.EmvTransactionActivity;
|
||||
import com.example.bdkipoc.transaction.CreateTransactionActivity;
|
||||
import com.example.bdkipoc.kredit.CreditCardActivity;
|
||||
|
||||
public class MainActivity extends AppCompatActivity {
|
||||
|
@ -1,179 +0,0 @@
|
||||
package com.example.bdkipoc.kredit;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
|
||||
import com.example.bdkipoc.R;
|
||||
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* CreateTransactionActivity - Handle amount input and transaction creation
|
||||
*/
|
||||
public class CreateTransactionActivity extends AppCompatActivity {
|
||||
private static final String TAG = "CreateTransaction";
|
||||
|
||||
// UI Components
|
||||
private TextView tvAmountDisplay;
|
||||
private TextView tvModeIndicator;
|
||||
private Button btnConfirm;
|
||||
private Button btnToggleMode;
|
||||
|
||||
// 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; // Default to EMV mode
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_create_transaction);
|
||||
|
||||
initViews();
|
||||
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 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);
|
||||
|
||||
// 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 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
|
||||
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;
|
||||
}
|
||||
}
|
@ -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
|
||||
*/
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -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<EMVCandidateV2> 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<EMVCandidateV2> 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);
|
||||
}
|
||||
};
|
||||
}
|
@ -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");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
};
|
||||
}
|
@ -1,203 +1,276 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true"
|
||||
android:background="@color/colorBackground"
|
||||
tools:context=".kredit.CreateTransactionActivity">
|
||||
|
||||
<LinearLayout
|
||||
<!-- Main Content -->
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="match_parent"
|
||||
android:fillViewport="true">
|
||||
|
||||
<!-- Toolbar -->
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/primary_blue"
|
||||
android:theme="@style/CustomToolbarTheme"
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
<!-- Content Container -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Header Section -->
|
||||
<androidx.cardview.widget.CardView
|
||||
<!-- Toolbar -->
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/primary_blue"
|
||||
android:theme="@style/CustomToolbarTheme"
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
<!-- Content Container -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="4dp">
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<LinearLayout
|
||||
<!-- Header Section -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
android:layout_marginBottom="24dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="4dp">
|
||||
|
||||
<TextView
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="MASUKKAN NOMINAL"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#333333"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="16dp" />
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
|
||||
<!-- Amount Display -->
|
||||
<TextView
|
||||
android:id="@+id/tv_amount_display"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Rp 0,00"
|
||||
style="@style/AmountDisplayStyle"
|
||||
android:layout_marginBottom="8dp" />
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="MASUKKAN NOMINAL"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#333333"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="16dp" />
|
||||
|
||||
<!-- Mode Indicator -->
|
||||
<TextView
|
||||
android:id="@+id/tv_mode_indicator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Current Mode: EMV Mode (Full Card Data)"
|
||||
style="@style/ModeIndicatorStyle" />
|
||||
<!-- Amount Display -->
|
||||
<TextView
|
||||
android:id="@+id/tv_amount_display"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Rp 0,00"
|
||||
style="@style/AmountDisplayStyle"
|
||||
android:layout_marginBottom="8dp" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
<!-- Mode Indicator -->
|
||||
<TextView
|
||||
android:id="@+id/tv_mode_indicator"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Current Mode: EMV Mode (Full Card Data)"
|
||||
style="@style/ModeIndicatorStyle" />
|
||||
|
||||
<!-- Mode Toggle Button -->
|
||||
<Button
|
||||
android:id="@+id/btn_toggle_mode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="Switch to Simple"
|
||||
style="@style/OutlineButton" />
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- Keypad Section -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="24dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="4dp">
|
||||
<!-- Mode Toggle Button -->
|
||||
<Button
|
||||
android:id="@+id/btn_toggle_mode"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:text="Switch to Simple"
|
||||
style="@style/OutlineButton" />
|
||||
|
||||
<LinearLayout
|
||||
<!-- Keypad Section -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
android:layout_marginBottom="24dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
app:cardElevation="4dp">
|
||||
|
||||
<TextView
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Keypad Input"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#333333"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="16dp" />
|
||||
android:orientation="vertical"
|
||||
android:padding="16dp">
|
||||
|
||||
<!-- Keypad Grid -->
|
||||
<GridLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:columnCount="3"
|
||||
android:rowCount="4"
|
||||
android:layout_gravity="center">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Keypad Input"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
android:textColor="#333333"
|
||||
android:gravity="center"
|
||||
android:layout_marginBottom="16dp" />
|
||||
|
||||
<!-- Row 1: 1, 2, 3 -->
|
||||
<Button
|
||||
android:id="@+id/btn_1"
|
||||
style="@style/KeypadButton"
|
||||
android:text="1" />
|
||||
<!-- Keypad Grid -->
|
||||
<GridLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:columnCount="3"
|
||||
android:rowCount="4"
|
||||
android:layout_gravity="center">
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_2"
|
||||
style="@style/KeypadButton"
|
||||
android:text="2" />
|
||||
<!-- Row 1: 1, 2, 3 -->
|
||||
<Button
|
||||
android:id="@+id/btn_1"
|
||||
style="@style/KeypadButton"
|
||||
android:text="1" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_3"
|
||||
style="@style/KeypadButton"
|
||||
android:text="3" />
|
||||
<Button
|
||||
android:id="@+id/btn_2"
|
||||
style="@style/KeypadButton"
|
||||
android:text="2" />
|
||||
|
||||
<!-- Row 2: 4, 5, 6 -->
|
||||
<Button
|
||||
android:id="@+id/btn_4"
|
||||
style="@style/KeypadButton"
|
||||
android:text="4" />
|
||||
<Button
|
||||
android:id="@+id/btn_3"
|
||||
style="@style/KeypadButton"
|
||||
android:text="3" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_5"
|
||||
style="@style/KeypadButton"
|
||||
android:text="5" />
|
||||
<!-- Row 2: 4, 5, 6 -->
|
||||
<Button
|
||||
android:id="@+id/btn_4"
|
||||
style="@style/KeypadButton"
|
||||
android:text="4" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_6"
|
||||
style="@style/KeypadButton"
|
||||
android:text="6" />
|
||||
<Button
|
||||
android:id="@+id/btn_5"
|
||||
style="@style/KeypadButton"
|
||||
android:text="5" />
|
||||
|
||||
<!-- Row 3: 7, 8, 9 -->
|
||||
<Button
|
||||
android:id="@+id/btn_7"
|
||||
style="@style/KeypadButton"
|
||||
android:text="7" />
|
||||
<Button
|
||||
android:id="@+id/btn_6"
|
||||
style="@style/KeypadButton"
|
||||
android:text="6" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_8"
|
||||
style="@style/KeypadButton"
|
||||
android:text="8" />
|
||||
<!-- Row 3: 7, 8, 9 -->
|
||||
<Button
|
||||
android:id="@+id/btn_7"
|
||||
style="@style/KeypadButton"
|
||||
android:text="7" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_9"
|
||||
style="@style/KeypadButton"
|
||||
android:text="9" />
|
||||
<Button
|
||||
android:id="@+id/btn_8"
|
||||
style="@style/KeypadButton"
|
||||
android:text="8" />
|
||||
|
||||
<!-- Row 4: 00, 0, Clear -->
|
||||
<Button
|
||||
android:id="@+id/btn_00"
|
||||
style="@style/KeypadButton"
|
||||
android:text="00" />
|
||||
<Button
|
||||
android:id="@+id/btn_9"
|
||||
style="@style/KeypadButton"
|
||||
android:text="9" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_0"
|
||||
style="@style/KeypadButton"
|
||||
android:text="0" />
|
||||
<!-- Row 4: 00, 0, Clear -->
|
||||
<Button
|
||||
android:id="@+id/btn_00"
|
||||
style="@style/KeypadButton"
|
||||
android:text="00" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_clear"
|
||||
style="@style/KeypadButtonSecondary"
|
||||
android:text="⌫" />
|
||||
<Button
|
||||
android:id="@+id/btn_0"
|
||||
style="@style/KeypadButton"
|
||||
android:text="0" />
|
||||
|
||||
</GridLayout>
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
<Button
|
||||
android:id="@+id/btn_clear"
|
||||
style="@style/KeypadButtonSecondary"
|
||||
android:text="⌫" />
|
||||
|
||||
<!-- Confirm Button -->
|
||||
<Button
|
||||
android:id="@+id/btn_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:text="KONFIRMASI NOMINAL"
|
||||
style="@style/PrimaryButton"
|
||||
android:layout_marginBottom="16dp" />
|
||||
</GridLayout>
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
<!-- Instructions -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="• Gunakan keypad untuk memasukkan nominal\n• Minimal transaksi Rp 1,00\n• Pilih mode EMV untuk data lengkap kartu"
|
||||
style="@style/HintTextStyle" />
|
||||
<!-- Confirm Button -->
|
||||
<Button
|
||||
android:id="@+id/btn_confirm"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="56dp"
|
||||
android:text="KONFIRMASI NOMINAL"
|
||||
style="@style/PrimaryButton"
|
||||
android:layout_marginBottom="16dp" />
|
||||
|
||||
<!-- Instructions -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="• Gunakan keypad untuk memasukkan nominal\n• Minimal transaksi Rp 1,00\n• Pilih mode EMV untuk data lengkap kartu"
|
||||
style="@style/HintTextStyle" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</ScrollView>
|
||||
|
||||
<!-- Hidden Progress Bar for Card Scanning -->
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:indeterminateTint="@color/primary_blue" />
|
||||
|
||||
<!-- Modal Overlay with Blur Effect for Card Scanning -->
|
||||
<FrameLayout
|
||||
android:id="@+id/modal_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#80000000"
|
||||
android:visibility="gone"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<!-- Modal Content -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/modal_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="32dp"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="8dp"
|
||||
app:cardBackgroundColor="@android:color/white">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="32dp"
|
||||
android:gravity="center">
|
||||
|
||||
<!-- Card Icon -->
|
||||
<ImageView
|
||||
android:id="@+id/modal_icon"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:src="@drawable/ic_card_insert"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:adjustViewBounds="true"
|
||||
app:tint="@color/primary_blue" />
|
||||
|
||||
<!-- Main Text -->
|
||||
<TextView
|
||||
android:id="@+id/modal_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Silakan Tempelkan / Gesekkan / Masukkan Kartu ke Perangkat"
|
||||
style="@style/StatusTextStyle"
|
||||
android:textAlignment="center"
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="4dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
@ -1,160 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/colorBackground"
|
||||
tools:context=".kredit.EmvTransactionActivity">
|
||||
|
||||
<!-- Main Content -->
|
||||
<LinearLayout
|
||||
android:id="@+id/main_content"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<!-- Toolbar -->
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:background="@color/primary_blue"
|
||||
android:theme="@style/CustomToolbarTheme"
|
||||
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
|
||||
|
||||
<!-- Main Content Container with Center Alignment -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:padding="24dp">
|
||||
|
||||
<!-- Status Modal Card - Centered and Compact -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="8dp"
|
||||
app:cardBackgroundColor="@android:color/white">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="32dp"
|
||||
android:gravity="center">
|
||||
|
||||
<!-- Header Title -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Card Scanner"
|
||||
style="@style/HeaderTextStyle"
|
||||
android:layout_marginBottom="24dp" />
|
||||
|
||||
<!-- Card Reader Animation/Icon -->
|
||||
<ImageView
|
||||
android:id="@+id/iv_card_reader"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:src="@drawable/ic_card_insert"
|
||||
android:scaleType="centerInside"
|
||||
app:tint="@color/primary_blue" />
|
||||
|
||||
<!-- Status Text -->
|
||||
<TextView
|
||||
android:id="@+id/tv_status"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Ready for card scanning..."
|
||||
style="@style/StatusTextStyle"
|
||||
android:layout_marginBottom="24dp" />
|
||||
|
||||
<!-- Progress Indicator -->
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_bar"
|
||||
style="?android:attr/progressBarStyle"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="visible"
|
||||
android:indeterminateTint="@color/primary_blue"
|
||||
android:layout_marginBottom="16dp" />
|
||||
|
||||
<!-- Instructions Text -->
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Please insert, tap, or swipe your card\nScanning will start automatically"
|
||||
style="@style/HintTextStyle"
|
||||
android:gravity="center" />
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Modal Overlay with Blur Effect -->
|
||||
<FrameLayout
|
||||
android:id="@+id/modal_overlay"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="#80000000"
|
||||
android:visibility="gone"
|
||||
android:clickable="true"
|
||||
android:focusable="true">
|
||||
|
||||
<!-- Modal Content -->
|
||||
<androidx.cardview.widget.CardView
|
||||
android:id="@+id/modal_card"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_margin="32dp"
|
||||
app:cardCornerRadius="16dp"
|
||||
app:cardElevation="8dp"
|
||||
app:cardBackgroundColor="@android:color/white">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="32dp"
|
||||
android:gravity="center">
|
||||
|
||||
<!-- Card Icon -->
|
||||
<ImageView
|
||||
android:id="@+id/modal_icon"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="80dp"
|
||||
android:src="@drawable/ic_card_insert"
|
||||
android:layout_marginBottom="24dp"
|
||||
android:scaleType="fitCenter"
|
||||
android:adjustViewBounds="true"
|
||||
app:tint="@color/primary_blue" />
|
||||
|
||||
<!-- Main Text -->
|
||||
<TextView
|
||||
android:id="@+id/modal_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Silakan Tempelkan / Gesekkan / Masukkan Kartu ke Perangkat"
|
||||
style="@style/StatusTextStyle"
|
||||
android:textAlignment="center"
|
||||
android:gravity="center"
|
||||
android:lineSpacingExtra="4dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</androidx.cardview.widget.CardView>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
</FrameLayout>
|
Loading…
x
Reference in New Issue
Block a user