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