refactor
This commit is contained in:
		
							parent
							
								
									f5d9e53118
								
							
						
					
					
						commit
						f2c3de9f5f
					
				@ -45,29 +45,32 @@
 | 
				
			|||||||
            </intent-filter>
 | 
					            </intent-filter>
 | 
				
			||||||
        </activity>
 | 
					        </activity>
 | 
				
			||||||
        <activity
 | 
					        <activity
 | 
				
			||||||
            android:name=".TransactionActivity"
 | 
					            android:name=".cetakulang.ReprintActivity"
 | 
				
			||||||
            android:exported="false" />
 | 
					            android:exported="false" />
 | 
				
			||||||
        <activity
 | 
					        <activity
 | 
				
			||||||
            android:name=".PaymentActivity"
 | 
					            android:name=".cetakulang.ReprintAdapterActivity"
 | 
				
			||||||
            android:exported="false" />
 | 
					 | 
				
			||||||
        <activity
 | 
					 | 
				
			||||||
            android:name=".PinActivity"
 | 
					 | 
				
			||||||
            android:screenOrientation="portrait"
 | 
					 | 
				
			||||||
            android:theme="@style/Theme.AppCompat.Light.NoActionBar"
 | 
					 | 
				
			||||||
            android:exported="false" />
 | 
					            android:exported="false" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <activity
 | 
					        <activity
 | 
				
			||||||
            android:name=".ReceiptActivity"
 | 
					            android:name=".ReceiptActivity"
 | 
				
			||||||
            android:exported="false" />
 | 
					            android:exported="false" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <activity
 | 
					        <activity
 | 
				
			||||||
            android:name=".QrisActivity"
 | 
					            android:name=".QrisActivity"
 | 
				
			||||||
            android:exported="false" />
 | 
					            android:exported="false" />
 | 
				
			||||||
        <activity android:name=".QrisResultActivity" />
 | 
					
 | 
				
			||||||
 | 
					        <activity 
 | 
				
			||||||
 | 
					            android:name=".QrisResultActivity"            
 | 
				
			||||||
 | 
					            android:exported="false" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <activity
 | 
					        <activity
 | 
				
			||||||
            android:name=".SettlementActivity"
 | 
					            android:name=".SettlementActivity"
 | 
				
			||||||
            android:exported="false" />
 | 
					            android:exported="false" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <activity
 | 
					        <activity
 | 
				
			||||||
            android:name=".HistoryActivity"
 | 
					            android:name=".HistoryActivity"
 | 
				
			||||||
            android:exported="false" />
 | 
					            android:exported="false" />
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
        <activity
 | 
					        <activity
 | 
				
			||||||
            android:name=".HistoryDetailActivity"
 | 
					            android:name=".HistoryDetailActivity"
 | 
				
			||||||
            android:exported="false" />
 | 
					            android:exported="false" />
 | 
				
			||||||
@ -77,7 +80,7 @@
 | 
				
			|||||||
            android:exported="false" />                
 | 
					            android:exported="false" />                
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        <activity
 | 
					        <activity
 | 
				
			||||||
            android:name=".kredit.CreditCardActivity"
 | 
					            android:name=".transaction.ResultTransactionActivity"
 | 
				
			||||||
            android:exported="false" />
 | 
					            android:exported="false" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <activity android:name="com.sunmi.emv.l2.view.AppSelectActivity"/>
 | 
					        <activity android:name="com.sunmi.emv.l2.view.AppSelectActivity"/>
 | 
				
			||||||
 | 
				
			|||||||
@ -19,8 +19,11 @@ import androidx.core.view.WindowInsetsCompat;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import com.google.android.material.button.MaterialButton;
 | 
					import com.google.android.material.button.MaterialButton;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.cetakulang.ReprintActivity;
 | 
				
			||||||
 | 
					import com.example.bdkipoc.cetakulang.ReprintAdapterActivity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.example.bdkipoc.transaction.CreateTransactionActivity;
 | 
					import com.example.bdkipoc.transaction.CreateTransactionActivity;
 | 
				
			||||||
import com.example.bdkipoc.kredit.CreditCardActivity;
 | 
					import com.example.bdkipoc.transaction.ResultTransactionActivity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class MainActivity extends AppCompatActivity {
 | 
					public class MainActivity extends AppCompatActivity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -86,12 +89,9 @@ public class MainActivity extends AppCompatActivity {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // 6 dummy menus should be hidden initially
 | 
					        // 6 dummy menus should be hidden initially
 | 
				
			||||||
        CardView[] dummyCards = {
 | 
					        CardView[] dummyCards = {
 | 
				
			||||||
            findViewById(R.id.card_dummy_menu_1),
 | 
					            findViewById(R.id.card_bantuan),
 | 
				
			||||||
            findViewById(R.id.card_dummy_menu_2),
 | 
					            findViewById(R.id.card_info_toko),
 | 
				
			||||||
            findViewById(R.id.card_dummy_menu_3),
 | 
					            findViewById(R.id.card_pengaturan),
 | 
				
			||||||
            findViewById(R.id.card_dummy_menu_4),
 | 
					 | 
				
			||||||
            findViewById(R.id.card_dummy_menu_5),
 | 
					 | 
				
			||||||
            findViewById(R.id.card_dummy_menu_6)
 | 
					 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (CardView card : dummyCards) {
 | 
					        for (CardView card : dummyCards) {
 | 
				
			||||||
@ -138,21 +138,17 @@ public class MainActivity extends AppCompatActivity {
 | 
				
			|||||||
            R.id.card_kartu_debit,
 | 
					            R.id.card_kartu_debit,
 | 
				
			||||||
            R.id.card_qris,
 | 
					            R.id.card_qris,
 | 
				
			||||||
            // Row 2 (Always visible - 3 items)
 | 
					            // Row 2 (Always visible - 3 items)
 | 
				
			||||||
 | 
					            R.id.card_transfer,
 | 
				
			||||||
            R.id.card_uang_elektronik,
 | 
					            R.id.card_uang_elektronik,
 | 
				
			||||||
            R.id.card_cetak_ulang,
 | 
					            R.id.card_cetak_ulang,
 | 
				
			||||||
            R.id.card_settlement,
 | 
					 | 
				
			||||||
            // Row 3 (Always visible - 3 items)
 | 
					            // Row 3 (Always visible - 3 items)
 | 
				
			||||||
 | 
					            R.id.card_refund,
 | 
				
			||||||
 | 
					            R.id.card_settlement,
 | 
				
			||||||
            R.id.card_histori,
 | 
					            R.id.card_histori,
 | 
				
			||||||
 | 
					            // Row 4 (Hidden initially - 3 items)
 | 
				
			||||||
            R.id.card_bantuan,
 | 
					            R.id.card_bantuan,
 | 
				
			||||||
            R.id.card_info_toko,            
 | 
					            R.id.card_info_toko,            
 | 
				
			||||||
            // Row 4 (Hidden initially - 3 items)
 | 
					            R.id.card_pengaturan,
 | 
				
			||||||
            R.id.card_dummy_menu_1,
 | 
					 | 
				
			||||||
            R.id.card_dummy_menu_2,
 | 
					 | 
				
			||||||
            R.id.card_dummy_menu_3,
 | 
					 | 
				
			||||||
            // Row 5 (Hidden initially - 3 items)
 | 
					 | 
				
			||||||
            R.id.card_dummy_menu_4,
 | 
					 | 
				
			||||||
            R.id.card_dummy_menu_5,
 | 
					 | 
				
			||||||
            R.id.card_dummy_menu_6
 | 
					 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Set up click listeners for all cards
 | 
					        // Set up click listeners for all cards
 | 
				
			||||||
@ -163,33 +159,30 @@ public class MainActivity extends AppCompatActivity {
 | 
				
			|||||||
                    if (cardId == R.id.card_kartu_kredit) {
 | 
					                    if (cardId == R.id.card_kartu_kredit) {
 | 
				
			||||||
                        startActivity(new Intent(MainActivity.this, CreateTransactionActivity.class));
 | 
					                        startActivity(new Intent(MainActivity.this, CreateTransactionActivity.class));
 | 
				
			||||||
                    } else if (cardId == R.id.card_kartu_debit) {
 | 
					                    } else if (cardId == R.id.card_kartu_debit) {
 | 
				
			||||||
                        startActivity(new Intent(MainActivity.this, PaymentActivity.class));
 | 
					                        startActivity(new Intent(MainActivity.this, CreateTransactionActivity.class));
 | 
				
			||||||
                    } else if (cardId == R.id.card_qris) {
 | 
					                    } else if (cardId == R.id.card_qris) {
 | 
				
			||||||
                        startActivity(new Intent(MainActivity.this, QrisActivity.class));
 | 
					                        startActivity(new Intent(MainActivity.this, QrisActivity.class));
 | 
				
			||||||
 | 
					                    // Col-2
 | 
				
			||||||
 | 
					                    } else if (cardId == R.id.card_transfer) {
 | 
				
			||||||
 | 
					                        startActivity(new Intent(MainActivity.this, CreateTransactionActivity.class));
 | 
				
			||||||
                    } else if (cardId == R.id.card_uang_elektronik) {
 | 
					                    } else if (cardId == R.id.card_uang_elektronik) {
 | 
				
			||||||
                        startActivity(new Intent(MainActivity.this, PaymentActivity.class));
 | 
					                        startActivity(new Intent(MainActivity.this, CreateTransactionActivity.class));
 | 
				
			||||||
                    } else if (cardId == R.id.card_cetak_ulang) {
 | 
					                    } else if (cardId == R.id.card_cetak_ulang) {
 | 
				
			||||||
                        startActivity(new Intent(MainActivity.this, TransactionActivity.class));
 | 
					                        startActivity(new Intent(MainActivity.this, ReprintActivity.class));
 | 
				
			||||||
 | 
					                    // Col-3
 | 
				
			||||||
 | 
					                    } else if (cardId == R.id.card_refund) {
 | 
				
			||||||
 | 
					                        startActivity(new Intent(MainActivity.this, CreateTransactionActivity.class));
 | 
				
			||||||
                    } else if (cardId == R.id.card_settlement) {
 | 
					                    } else if (cardId == R.id.card_settlement) {
 | 
				
			||||||
                        Toast.makeText(this, "Settlement - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
					                        Toast.makeText(this, "Settlement - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
				
			||||||
                    } else if (cardId == R.id.card_histori) {
 | 
					                    } else if (cardId == R.id.card_histori) {
 | 
				
			||||||
                        Toast.makeText(this, "Histori - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
					                        startActivity(new Intent(MainActivity.this, HistoryActivity.class));
 | 
				
			||||||
 | 
					                    // Col-4
 | 
				
			||||||
                    } else if (cardId == R.id.card_bantuan) {
 | 
					                    } else if (cardId == R.id.card_bantuan) {
 | 
				
			||||||
                        Toast.makeText(this, "Bantuan - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
					                        Toast.makeText(this, "Bantuan - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
				
			||||||
                    } else if (cardId == R.id.card_info_toko) {
 | 
					                    } else if (cardId == R.id.card_info_toko) {
 | 
				
			||||||
                        Toast.makeText(this, "Info Toko - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
					                        Toast.makeText(this, "Info Toko - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
				
			||||||
                    } else if (cardId == R.id.card_dummy_menu_1) {
 | 
					                    } else if (cardId == R.id.card_pengaturan) {
 | 
				
			||||||
                        Toast.makeText(this, "Dummy Menu 1 - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
					                        Toast.makeText(this, "Pengaturan - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
				
			||||||
                    } else if (cardId == R.id.card_dummy_menu_2) {
 | 
					 | 
				
			||||||
                        Toast.makeText(this, "Dummy Menu 2 - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
                    } else if (cardId == R.id.card_dummy_menu_3) {
 | 
					 | 
				
			||||||
                        Toast.makeText(this, "Dummy Menu 3 - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
                    } else if (cardId == R.id.card_dummy_menu_4) {
 | 
					 | 
				
			||||||
                        Toast.makeText(this, "Dummy Menu 4 - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
                    } else if (cardId == R.id.card_dummy_menu_5) {
 | 
					 | 
				
			||||||
                        Toast.makeText(this, "Dummy Menu 5 - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
                    } else if (cardId == R.id.card_dummy_menu_6) {
 | 
					 | 
				
			||||||
                        Toast.makeText(this, "Dummy Menu 6 - Coming Soon", Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
                    } else {
 | 
					                    } else {
 | 
				
			||||||
                        // Fallback for any other cards
 | 
					                        // Fallback for any other cards
 | 
				
			||||||
                        Toast.makeText(this, "Menu Diklik: " + getResources().getResourceEntryName(cardId), Toast.LENGTH_SHORT).show();
 | 
					                        Toast.makeText(this, "Menu Diklik: " + getResources().getResourceEntryName(cardId), Toast.LENGTH_SHORT).show();
 | 
				
			||||||
@ -200,12 +193,9 @@ public class MainActivity extends AppCompatActivity {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        // Get references to ONLY the dummy cards that need to be toggled
 | 
					        // Get references to ONLY the dummy cards that need to be toggled
 | 
				
			||||||
        CardView[] toggleableCards = {
 | 
					        CardView[] toggleableCards = {
 | 
				
			||||||
            findViewById(R.id.card_dummy_menu_1),
 | 
					            findViewById(R.id.card_bantuan),
 | 
				
			||||||
            findViewById(R.id.card_dummy_menu_2),
 | 
					            findViewById(R.id.card_info_toko),
 | 
				
			||||||
            findViewById(R.id.card_dummy_menu_3),
 | 
					            findViewById(R.id.card_pengaturan),
 | 
				
			||||||
            findViewById(R.id.card_dummy_menu_4),
 | 
					 | 
				
			||||||
            findViewById(R.id.card_dummy_menu_5),
 | 
					 | 
				
			||||||
            findViewById(R.id.card_dummy_menu_6)
 | 
					 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Set up "Lainnya" button click listener
 | 
					        // Set up "Lainnya" button click listener
 | 
				
			||||||
 | 
				
			|||||||
@ -1,530 +0,0 @@
 | 
				
			|||||||
package com.example.bdkipoc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.animation.AnimatorSet;
 | 
					 | 
				
			||||||
import android.animation.ObjectAnimator;
 | 
					 | 
				
			||||||
import android.app.Dialog;
 | 
					 | 
				
			||||||
import android.content.Intent;
 | 
					 | 
				
			||||||
import android.graphics.Color;
 | 
					 | 
				
			||||||
import android.graphics.drawable.ColorDrawable;
 | 
					 | 
				
			||||||
import android.os.Build;
 | 
					 | 
				
			||||||
import android.os.Bundle;
 | 
					 | 
				
			||||||
import android.os.Handler;
 | 
					 | 
				
			||||||
import android.os.Looper;
 | 
					 | 
				
			||||||
import android.text.TextUtils;
 | 
					 | 
				
			||||||
import android.view.View;
 | 
					 | 
				
			||||||
import android.view.Window;
 | 
					 | 
				
			||||||
import android.view.WindowManager;
 | 
					 | 
				
			||||||
import android.view.animation.AccelerateDecelerateInterpolator;
 | 
					 | 
				
			||||||
import android.widget.Button;
 | 
					 | 
				
			||||||
import android.widget.EditText;
 | 
					 | 
				
			||||||
import android.widget.ImageView;
 | 
					 | 
				
			||||||
import android.widget.LinearLayout;
 | 
					 | 
				
			||||||
import android.widget.RadioGroup;
 | 
					 | 
				
			||||||
import android.widget.TextView;
 | 
					 | 
				
			||||||
import android.widget.Toast;
 | 
					 | 
				
			||||||
import androidx.appcompat.app.AppCompatActivity;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class PaymentActivity extends AppCompatActivity {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Views
 | 
					 | 
				
			||||||
    private EditText editTextAmount;
 | 
					 | 
				
			||||||
    private Button confirmButton;
 | 
					 | 
				
			||||||
    private LinearLayout backNavigation;
 | 
					 | 
				
			||||||
    private ImageView backArrow;
 | 
					 | 
				
			||||||
    private TextView toolbarTitle;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Numpad buttons
 | 
					 | 
				
			||||||
    private TextView btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn0, btn000;
 | 
					 | 
				
			||||||
    private ImageView btnDelete;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Modal components
 | 
					 | 
				
			||||||
    private Dialog paymentModal;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Data
 | 
					 | 
				
			||||||
    private StringBuilder currentAmount = new StringBuilder();
 | 
					 | 
				
			||||||
    private static final int MAX_AMOUNT_LENGTH = 12;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Animation
 | 
					 | 
				
			||||||
    private Handler animationHandler = new Handler(Looper.getMainLooper());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
					 | 
				
			||||||
        super.onCreate(savedInstanceState);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Set status bar color programmatically
 | 
					 | 
				
			||||||
        setStatusBarColor();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        setContentView(R.layout.activity_payment);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        initializeViews();
 | 
					 | 
				
			||||||
        setupClickListeners();
 | 
					 | 
				
			||||||
        setupInitialStates();
 | 
					 | 
				
			||||||
        setupModal();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void setStatusBarColor() {
 | 
					 | 
				
			||||||
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 | 
					 | 
				
			||||||
            Window window = getWindow();
 | 
					 | 
				
			||||||
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
 | 
					 | 
				
			||||||
            window.setStatusBarColor(Color.parseColor("#E31937")); // Red color
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Make status bar icons white (for dark red background)
 | 
					 | 
				
			||||||
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 | 
					 | 
				
			||||||
                View decorView = window.getDecorView();
 | 
					 | 
				
			||||||
                decorView.setSystemUiVisibility(0); // Clear light status bar flag
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void initializeViews() {
 | 
					 | 
				
			||||||
        // Main views
 | 
					 | 
				
			||||||
        editTextAmount = findViewById(R.id.editTextAmount);
 | 
					 | 
				
			||||||
        confirmButton = findViewById(R.id.confirmButton);
 | 
					 | 
				
			||||||
        backNavigation = findViewById(R.id.back_navigation);
 | 
					 | 
				
			||||||
        backArrow = findViewById(R.id.backArrow);
 | 
					 | 
				
			||||||
        toolbarTitle = findViewById(R.id.toolbarTitle);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Numpad buttons
 | 
					 | 
				
			||||||
        btn1 = findViewById(R.id.btn1);
 | 
					 | 
				
			||||||
        btn2 = findViewById(R.id.btn2);
 | 
					 | 
				
			||||||
        btn3 = findViewById(R.id.btn3);
 | 
					 | 
				
			||||||
        btn4 = findViewById(R.id.btn4);
 | 
					 | 
				
			||||||
        btn5 = findViewById(R.id.btn5);
 | 
					 | 
				
			||||||
        btn6 = findViewById(R.id.btn6);
 | 
					 | 
				
			||||||
        btn7 = findViewById(R.id.btn7);
 | 
					 | 
				
			||||||
        btn8 = findViewById(R.id.btn8);
 | 
					 | 
				
			||||||
        btn9 = findViewById(R.id.btn9);
 | 
					 | 
				
			||||||
        btn0 = findViewById(R.id.btn0);
 | 
					 | 
				
			||||||
        btn000 = findViewById(R.id.btn000);
 | 
					 | 
				
			||||||
        btnDelete = findViewById(R.id.btnDelete);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void setupModal() {
 | 
					 | 
				
			||||||
        // Create modal dialog
 | 
					 | 
				
			||||||
        paymentModal = new Dialog(this);
 | 
					 | 
				
			||||||
        paymentModal.setContentView(R.layout.modal_layout);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Remove background dimming - make it fully transparent
 | 
					 | 
				
			||||||
        if (paymentModal.getWindow() != null) {
 | 
					 | 
				
			||||||
            paymentModal.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Setup modal listeners
 | 
					 | 
				
			||||||
        setupModalListeners();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void setupModalListeners() {
 | 
					 | 
				
			||||||
        // Make modal non-cancelable by touching outside
 | 
					 | 
				
			||||||
        paymentModal.setCanceledOnTouchOutside(false);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Auto dismiss after 3 seconds (simulate card processing)
 | 
					 | 
				
			||||||
        Handler modalHandler = new Handler(Looper.getMainLooper());
 | 
					 | 
				
			||||||
        paymentModal.setOnShowListener(dialog -> {
 | 
					 | 
				
			||||||
            modalHandler.postDelayed(() -> {
 | 
					 | 
				
			||||||
                if (paymentModal != null && paymentModal.isShowing()) {
 | 
					 | 
				
			||||||
                    // First dismiss modal, then navigate
 | 
					 | 
				
			||||||
                    paymentModal.dismiss();
 | 
					 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                    // Add small delay to ensure modal is fully dismissed
 | 
					 | 
				
			||||||
                    animationHandler.postDelayed(() -> {
 | 
					 | 
				
			||||||
                        navigateToPinActivity();
 | 
					 | 
				
			||||||
                    }, 100);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }, 3000);
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void setupClickListeners() {
 | 
					 | 
				
			||||||
        // Back navigation - entire LinearLayout is clickable
 | 
					 | 
				
			||||||
        backNavigation.setOnClickListener(v -> {
 | 
					 | 
				
			||||||
            addClickAnimation(v);
 | 
					 | 
				
			||||||
            navigateBack();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Individual back arrow (for additional touch area)
 | 
					 | 
				
			||||||
        backArrow.setOnClickListener(v -> {
 | 
					 | 
				
			||||||
            addClickAnimation(v);
 | 
					 | 
				
			||||||
            navigateBack();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Toolbar title (also clickable for back navigation)
 | 
					 | 
				
			||||||
        toolbarTitle.setOnClickListener(v -> {
 | 
					 | 
				
			||||||
            addClickAnimation(v);
 | 
					 | 
				
			||||||
            navigateBack();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Numpad listeners
 | 
					 | 
				
			||||||
        btn1.setOnClickListener(v -> handleNumpadClick(v, "1"));
 | 
					 | 
				
			||||||
        btn2.setOnClickListener(v -> handleNumpadClick(v, "2"));
 | 
					 | 
				
			||||||
        btn3.setOnClickListener(v -> handleNumpadClick(v, "3"));
 | 
					 | 
				
			||||||
        btn4.setOnClickListener(v -> handleNumpadClick(v, "4"));
 | 
					 | 
				
			||||||
        btn5.setOnClickListener(v -> handleNumpadClick(v, "5"));
 | 
					 | 
				
			||||||
        btn6.setOnClickListener(v -> handleNumpadClick(v, "6"));
 | 
					 | 
				
			||||||
        btn7.setOnClickListener(v -> handleNumpadClick(v, "7"));
 | 
					 | 
				
			||||||
        btn8.setOnClickListener(v -> handleNumpadClick(v, "8"));
 | 
					 | 
				
			||||||
        btn9.setOnClickListener(v -> handleNumpadClick(v, "9"));
 | 
					 | 
				
			||||||
        btn0.setOnClickListener(v -> handleNumpadClick(v, "0"));
 | 
					 | 
				
			||||||
        btn000.setOnClickListener(v -> handleNumpadClick(v, "000"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Delete button
 | 
					 | 
				
			||||||
        btnDelete.setOnClickListener(v -> {
 | 
					 | 
				
			||||||
            addClickAnimation(v);
 | 
					 | 
				
			||||||
            deleteLastDigit();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Confirm button - NOW SHOWS MODAL INSTEAD OF DIRECT PAYMENT
 | 
					 | 
				
			||||||
        confirmButton.setOnClickListener(v -> {
 | 
					 | 
				
			||||||
            if (confirmButton.isEnabled()) {
 | 
					 | 
				
			||||||
                addButtonClickAnimation(v);
 | 
					 | 
				
			||||||
                showPaymentModal();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void navigateBack() {
 | 
					 | 
				
			||||||
        // Simple back navigation without card animation
 | 
					 | 
				
			||||||
        finish();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void handleNumpadClick(View view, String digit) {
 | 
					 | 
				
			||||||
        addClickAnimation(view);
 | 
					 | 
				
			||||||
        addDigit(digit);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void setupInitialStates() {
 | 
					 | 
				
			||||||
        // Set initial amount display
 | 
					 | 
				
			||||||
        editTextAmount.setText("");
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Set initial button state
 | 
					 | 
				
			||||||
        updateButtonState();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Disable EditText input (only numpad input allowed)
 | 
					 | 
				
			||||||
        editTextAmount.setFocusable(false);
 | 
					 | 
				
			||||||
        editTextAmount.setClickable(false);
 | 
					 | 
				
			||||||
        editTextAmount.setCursorVisible(false);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void addDigit(String digit) {
 | 
					 | 
				
			||||||
        // Validate input length
 | 
					 | 
				
			||||||
        if (currentAmount.length() >= MAX_AMOUNT_LENGTH) {
 | 
					 | 
				
			||||||
            showToast("Maksimal " + MAX_AMOUNT_LENGTH + " digit");
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Handle leading zeros
 | 
					 | 
				
			||||||
        if (currentAmount.length() == 0) {
 | 
					 | 
				
			||||||
            if (digit.equals("000")) {
 | 
					 | 
				
			||||||
                // Don't allow 000 as first input
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            currentAmount.append(digit);
 | 
					 | 
				
			||||||
        } else if (currentAmount.length() == 1 && currentAmount.toString().equals("0")) {
 | 
					 | 
				
			||||||
            if (!digit.equals("000")) {
 | 
					 | 
				
			||||||
                // Replace single 0 with new digit
 | 
					 | 
				
			||||||
                currentAmount = new StringBuilder(digit);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            currentAmount.append(digit);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        updateAmountDisplay();
 | 
					 | 
				
			||||||
        updateButtonState();
 | 
					 | 
				
			||||||
        addInputFeedback();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void deleteLastDigit() {
 | 
					 | 
				
			||||||
        if (currentAmount.length() > 0) {
 | 
					 | 
				
			||||||
            String current = currentAmount.toString();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // If current ends with 000, remove all three digits
 | 
					 | 
				
			||||||
            if (current.endsWith("000") && current.length() >= 3) {
 | 
					 | 
				
			||||||
                currentAmount.delete(currentAmount.length() - 3, currentAmount.length());
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                currentAmount.deleteCharAt(currentAmount.length() - 1);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            updateAmountDisplay();
 | 
					 | 
				
			||||||
            updateButtonState();
 | 
					 | 
				
			||||||
            addDeleteFeedback();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void updateAmountDisplay() {
 | 
					 | 
				
			||||||
        String amount = currentAmount.toString();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        if (amount.isEmpty() || amount.equals("0")) {
 | 
					 | 
				
			||||||
            editTextAmount.setText("");
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            String formattedAmount = formatCurrency(amount);
 | 
					 | 
				
			||||||
            editTextAmount.setText(formattedAmount);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private String formatCurrency(String amount) {
 | 
					 | 
				
			||||||
        if (TextUtils.isEmpty(amount) || amount.equals("0")) {
 | 
					 | 
				
			||||||
            return "";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            long number = Long.parseLong(amount);
 | 
					 | 
				
			||||||
            return String.format("%,d", number).replace(',', '.');
 | 
					 | 
				
			||||||
        } catch (NumberFormatException e) {
 | 
					 | 
				
			||||||
            return amount;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void updateButtonState() {
 | 
					 | 
				
			||||||
        boolean hasValidAmount = currentAmount.length() > 0 && 
 | 
					 | 
				
			||||||
                                !currentAmount.toString().equals("0") &&
 | 
					 | 
				
			||||||
                                !currentAmount.toString().isEmpty();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        confirmButton.setEnabled(hasValidAmount);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        if (hasValidAmount) {
 | 
					 | 
				
			||||||
            // Active state
 | 
					 | 
				
			||||||
            confirmButton.setBackgroundResource(R.drawable.button_active_background);
 | 
					 | 
				
			||||||
            confirmButton.setTextColor(Color.WHITE);
 | 
					 | 
				
			||||||
            confirmButton.setAlpha(1.0f);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            // Inactive state
 | 
					 | 
				
			||||||
            confirmButton.setBackgroundResource(R.drawable.button_inactive_background);
 | 
					 | 
				
			||||||
            confirmButton.setTextColor(Color.parseColor("#999999"));
 | 
					 | 
				
			||||||
            confirmButton.setAlpha(0.6f);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // NEW METHOD: Show payment modal instead of direct payment processing
 | 
					 | 
				
			||||||
    private void showPaymentModal() {
 | 
					 | 
				
			||||||
        String amount = currentAmount.toString();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        if (TextUtils.isEmpty(amount) || amount.equals("0")) {
 | 
					 | 
				
			||||||
            showToast("Masukkan jumlah pembayaran");
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            long amountValue = Long.parseLong(amount);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Validate minimum amount
 | 
					 | 
				
			||||||
            if (amountValue < 1000) {
 | 
					 | 
				
			||||||
                showToast("Minimal pembayaran Rp 1.000");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Validate maximum amount
 | 
					 | 
				
			||||||
            if (amountValue > 999999999L) {
 | 
					 | 
				
			||||||
                showToast("Maksimal pembayaran Rp 999.999.999");
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            // Show modal with animation
 | 
					 | 
				
			||||||
            showModalWithAnimation();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
        } catch (NumberFormatException e) {
 | 
					 | 
				
			||||||
            showToast("Format jumlah tidak valid");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void showModalWithAnimation() {
 | 
					 | 
				
			||||||
        // Add debug log
 | 
					 | 
				
			||||||
        showToast("Showing card modal...");
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        paymentModal.show();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Add slide-up animation
 | 
					 | 
				
			||||||
        View modalView = paymentModal.findViewById(android.R.id.content);
 | 
					 | 
				
			||||||
        if (modalView != null) {
 | 
					 | 
				
			||||||
            ObjectAnimator slideUp = ObjectAnimator.ofFloat(modalView, "translationY", 300f, 0f);
 | 
					 | 
				
			||||||
            ObjectAnimator fadeIn = ObjectAnimator.ofFloat(modalView, "alpha", 0f, 1f);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            AnimatorSet animatorSet = new AnimatorSet();
 | 
					 | 
				
			||||||
            animatorSet.playTogether(slideUp, fadeIn);
 | 
					 | 
				
			||||||
            animatorSet.setDuration(300);
 | 
					 | 
				
			||||||
            animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
 | 
					 | 
				
			||||||
            animatorSet.start();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void dismissModal() {
 | 
					 | 
				
			||||||
        if (paymentModal != null && paymentModal.isShowing()) {
 | 
					 | 
				
			||||||
            // Add slide-down animation before dismissing
 | 
					 | 
				
			||||||
            View modalView = paymentModal.findViewById(android.R.id.content);
 | 
					 | 
				
			||||||
            if (modalView != null) {
 | 
					 | 
				
			||||||
                ObjectAnimator slideDown = ObjectAnimator.ofFloat(modalView, "translationY", 0f, 300f);
 | 
					 | 
				
			||||||
                ObjectAnimator fadeOut = ObjectAnimator.ofFloat(modalView, "alpha", 1f, 0f);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                AnimatorSet animatorSet = new AnimatorSet();
 | 
					 | 
				
			||||||
                animatorSet.playTogether(slideDown, fadeOut);
 | 
					 | 
				
			||||||
                animatorSet.setDuration(200);
 | 
					 | 
				
			||||||
                animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                animatorSet.addListener(new android.animation.AnimatorListenerAdapter() {
 | 
					 | 
				
			||||||
                    @Override
 | 
					 | 
				
			||||||
                    public void onAnimationEnd(android.animation.Animator animation) {
 | 
					 | 
				
			||||||
                        paymentModal.dismiss();
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                animatorSet.start();
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                paymentModal.dismiss();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void processModalConfirmation() {
 | 
					 | 
				
			||||||
        // This method is no longer needed since modal auto-dismisses
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void navigateToPinActivity() {
 | 
					 | 
				
			||||||
        String amount = currentAmount.toString();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Add debug log
 | 
					 | 
				
			||||||
        showToast("Navigating to PIN Activity...");
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            long amountValue = Long.parseLong(amount);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Launch PIN Activity with amount data
 | 
					 | 
				
			||||||
            Intent intent = new Intent(this, PinActivity.class);
 | 
					 | 
				
			||||||
            intent.putExtra(PinActivity.EXTRA_SOURCE_ACTIVITY, "PaymentActivity");
 | 
					 | 
				
			||||||
            intent.putExtra(PinActivity.EXTRA_AMOUNT, String.valueOf(amountValue));
 | 
					 | 
				
			||||||
            startActivityForResult(intent, 100);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
        } catch (NumberFormatException e) {
 | 
					 | 
				
			||||||
            showToast("Format jumlah tidak valid: " + e.getMessage());
 | 
					 | 
				
			||||||
        } catch (Exception e) {
 | 
					 | 
				
			||||||
            showToast("Error navigating to PIN: " + e.getMessage());
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void processCardPayment() {
 | 
					 | 
				
			||||||
        // This method is called after PIN verification is successful
 | 
					 | 
				
			||||||
        // Now process the actual payment
 | 
					 | 
				
			||||||
        String amount = currentAmount.toString();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            long amountValue = Long.parseLong(amount);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Show processing message
 | 
					 | 
				
			||||||
            showToast("PIN berhasil diverifikasi! Memproses pembayaran...");
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Process the final payment
 | 
					 | 
				
			||||||
            processPayment(amountValue);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
        } catch (NumberFormatException e) {
 | 
					 | 
				
			||||||
            showToast("Format jumlah tidak valid");
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
     
 | 
					 | 
				
			||||||
    private void processPayment(long amount) {
 | 
					 | 
				
			||||||
        // Show loading state
 | 
					 | 
				
			||||||
        confirmButton.setText("Memproses...");
 | 
					 | 
				
			||||||
        confirmButton.setEnabled(false);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Simulate payment processing
 | 
					 | 
				
			||||||
        animationHandler.postDelayed(() -> {
 | 
					 | 
				
			||||||
            // Show success message
 | 
					 | 
				
			||||||
            showToast("Pembayaran berhasil! Jumlah: Rp " + formatCurrency(String.valueOf(amount)));
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Reset state and go back (this is final step after PIN verification)
 | 
					 | 
				
			||||||
            resetPaymentState();
 | 
					 | 
				
			||||||
            navigateBack();
 | 
					 | 
				
			||||||
        }, 2000);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    private void resetPaymentState() {
 | 
					 | 
				
			||||||
        currentAmount = new StringBuilder();
 | 
					 | 
				
			||||||
        updateAmountDisplay();
 | 
					 | 
				
			||||||
        updateButtonState();
 | 
					 | 
				
			||||||
        confirmButton.setText("Konfirmasi");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Animation methods (only for numpad interactions)
 | 
					 | 
				
			||||||
    private void addClickAnimation(View view) {
 | 
					 | 
				
			||||||
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.95f, 1f);
 | 
					 | 
				
			||||||
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.95f, 1f);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        AnimatorSet animatorSet = new AnimatorSet();
 | 
					 | 
				
			||||||
        animatorSet.playTogether(scaleX, scaleY);
 | 
					 | 
				
			||||||
        animatorSet.setDuration(150);
 | 
					 | 
				
			||||||
        animatorSet.start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void addButtonClickAnimation(View view) {
 | 
					 | 
				
			||||||
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.98f, 1f);
 | 
					 | 
				
			||||||
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.98f, 1f);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        AnimatorSet animatorSet = new AnimatorSet();
 | 
					 | 
				
			||||||
        animatorSet.playTogether(scaleX, scaleY);
 | 
					 | 
				
			||||||
        animatorSet.setDuration(200);
 | 
					 | 
				
			||||||
        animatorSet.start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void addInputFeedback() {
 | 
					 | 
				
			||||||
        ObjectAnimator fadeIn = ObjectAnimator.ofFloat(editTextAmount, "alpha", 0.7f, 1f);
 | 
					 | 
				
			||||||
        fadeIn.setDuration(200);
 | 
					 | 
				
			||||||
        fadeIn.start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void addDeleteFeedback() {
 | 
					 | 
				
			||||||
        ObjectAnimator shake = ObjectAnimator.ofFloat(editTextAmount, "translationX", 0f, -10f, 10f, 0f);
 | 
					 | 
				
			||||||
        shake.setDuration(300);
 | 
					 | 
				
			||||||
        shake.start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Utility methods
 | 
					 | 
				
			||||||
    private void showToast(String message) {
 | 
					 | 
				
			||||||
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void onBackPressed() {
 | 
					 | 
				
			||||||
        // Check if modal is showing, dismiss it first
 | 
					 | 
				
			||||||
        if (paymentModal != null && paymentModal.isShowing()) {
 | 
					 | 
				
			||||||
            dismissModal();
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            navigateBack();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        super.onBackPressed();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 | 
					 | 
				
			||||||
        super.onActivityResult(requestCode, resultCode, data);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Handle result from PIN Activity
 | 
					 | 
				
			||||||
        if (resultCode == RESULT_OK && data != null) {
 | 
					 | 
				
			||||||
            boolean pinVerified = data.getBooleanExtra("pin_verified", false);
 | 
					 | 
				
			||||||
            if (pinVerified) {
 | 
					 | 
				
			||||||
                // PIN verification successful, process payment
 | 
					 | 
				
			||||||
                processCardPayment();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void onDestroy() {
 | 
					 | 
				
			||||||
        super.onDestroy();
 | 
					 | 
				
			||||||
        if (animationHandler != null) {
 | 
					 | 
				
			||||||
            animationHandler.removeCallbacksAndMessages(null);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Clean up modal
 | 
					 | 
				
			||||||
        if (paymentModal != null && paymentModal.isShowing()) {
 | 
					 | 
				
			||||||
            paymentModal.dismiss();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Public methods for testing
 | 
					 | 
				
			||||||
    public String getCurrentAmount() {
 | 
					 | 
				
			||||||
        return currentAmount.toString();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public boolean isConfirmButtonEnabled() {
 | 
					 | 
				
			||||||
        return confirmButton.isEnabled();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,558 +0,0 @@
 | 
				
			|||||||
package com.example.bdkipoc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.animation.AnimatorSet;
 | 
					 | 
				
			||||||
import android.animation.ObjectAnimator;
 | 
					 | 
				
			||||||
import android.content.Intent;
 | 
					 | 
				
			||||||
import android.graphics.Color;
 | 
					 | 
				
			||||||
import android.os.Build;
 | 
					 | 
				
			||||||
import android.os.Bundle;
 | 
					 | 
				
			||||||
import android.os.Handler;
 | 
					 | 
				
			||||||
import android.os.Looper;
 | 
					 | 
				
			||||||
import android.text.TextUtils;
 | 
					 | 
				
			||||||
import android.view.View;
 | 
					 | 
				
			||||||
import android.view.Window;
 | 
					 | 
				
			||||||
import android.view.WindowManager;
 | 
					 | 
				
			||||||
import android.widget.Button;
 | 
					 | 
				
			||||||
import android.widget.EditText;
 | 
					 | 
				
			||||||
import android.widget.ImageView;
 | 
					 | 
				
			||||||
import android.widget.LinearLayout;
 | 
					 | 
				
			||||||
import android.widget.TextView;
 | 
					 | 
				
			||||||
import android.widget.Toast;
 | 
					 | 
				
			||||||
import androidx.appcompat.app.AppCompatActivity;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class PinActivity extends AppCompatActivity {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Intent Extra Keys
 | 
					 | 
				
			||||||
    public static final String EXTRA_TITLE = "extra_title";
 | 
					 | 
				
			||||||
    public static final String EXTRA_SUBTITLE = "extra_subtitle";
 | 
					 | 
				
			||||||
    public static final String EXTRA_AMOUNT = "extra_amount";
 | 
					 | 
				
			||||||
    public static final String EXTRA_SOURCE_ACTIVITY = "extra_source_activity";
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Views
 | 
					 | 
				
			||||||
    private EditText editTextPin;
 | 
					 | 
				
			||||||
    private Button confirmButton;
 | 
					 | 
				
			||||||
    private LinearLayout backNavigation;
 | 
					 | 
				
			||||||
    private ImageView backArrow;
 | 
					 | 
				
			||||||
    private TextView toolbarTitle;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Success screen views
 | 
					 | 
				
			||||||
    private View successScreen;
 | 
					 | 
				
			||||||
    private ImageView successIcon;
 | 
					 | 
				
			||||||
    private TextView successMessage;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Numpad buttons
 | 
					 | 
				
			||||||
    private TextView btn1, btn2, btn3, btn4, btn5, btn6, btn7, btn8, btn9, btn0, btn000;
 | 
					 | 
				
			||||||
    private ImageView btnDelete;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Data
 | 
					 | 
				
			||||||
    private StringBuilder currentPin = new StringBuilder();
 | 
					 | 
				
			||||||
    private static final int MAX_PIN_LENGTH = 6;
 | 
					 | 
				
			||||||
    private static final int MIN_PIN_LENGTH = 4;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Extra data from intent
 | 
					 | 
				
			||||||
    private String sourceActivity;
 | 
					 | 
				
			||||||
    private String amount;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Animation
 | 
					 | 
				
			||||||
    private Handler animationHandler = new Handler(Looper.getMainLooper());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
					 | 
				
			||||||
        super.onCreate(savedInstanceState);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Set status bar color programmatically
 | 
					 | 
				
			||||||
        setStatusBarColor();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        setContentView(R.layout.activity_pin);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Get intent extras
 | 
					 | 
				
			||||||
        getIntentExtras();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        initializeViews();
 | 
					 | 
				
			||||||
        setupClickListeners();
 | 
					 | 
				
			||||||
        setupInitialStates();
 | 
					 | 
				
			||||||
        setupSuccessScreen();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void setStatusBarColor() {
 | 
					 | 
				
			||||||
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
 | 
					 | 
				
			||||||
            Window window = getWindow();
 | 
					 | 
				
			||||||
            window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
 | 
					 | 
				
			||||||
            window.setStatusBarColor(Color.parseColor("#E31937")); // Red color
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Make status bar icons white (for dark red background)
 | 
					 | 
				
			||||||
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
 | 
					 | 
				
			||||||
                View decorView = window.getDecorView();
 | 
					 | 
				
			||||||
                decorView.setSystemUiVisibility(0); // Clear light status bar flag
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void getIntentExtras() {
 | 
					 | 
				
			||||||
        Intent intent = getIntent();
 | 
					 | 
				
			||||||
        if (intent != null) {
 | 
					 | 
				
			||||||
            sourceActivity = intent.getStringExtra(EXTRA_SOURCE_ACTIVITY);
 | 
					 | 
				
			||||||
            amount = intent.getStringExtra(EXTRA_AMOUNT);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void initializeViews() {
 | 
					 | 
				
			||||||
        // Main views
 | 
					 | 
				
			||||||
        editTextPin = findViewById(R.id.editTextPin);
 | 
					 | 
				
			||||||
        confirmButton = findViewById(R.id.confirmButton);
 | 
					 | 
				
			||||||
        backNavigation = findViewById(R.id.back_navigation);
 | 
					 | 
				
			||||||
        backArrow = findViewById(R.id.backArrow);
 | 
					 | 
				
			||||||
        toolbarTitle = findViewById(R.id.toolbarTitle);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Success screen views
 | 
					 | 
				
			||||||
        successScreen = findViewById(R.id.success_screen);
 | 
					 | 
				
			||||||
        successIcon = findViewById(R.id.success_icon);
 | 
					 | 
				
			||||||
        successMessage = findViewById(R.id.success_message);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Numpad buttons
 | 
					 | 
				
			||||||
        btn1 = findViewById(R.id.btn1);
 | 
					 | 
				
			||||||
        btn2 = findViewById(R.id.btn2);
 | 
					 | 
				
			||||||
        btn3 = findViewById(R.id.btn3);
 | 
					 | 
				
			||||||
        btn4 = findViewById(R.id.btn4);
 | 
					 | 
				
			||||||
        btn5 = findViewById(R.id.btn5);
 | 
					 | 
				
			||||||
        btn6 = findViewById(R.id.btn6);
 | 
					 | 
				
			||||||
        btn7 = findViewById(R.id.btn7);
 | 
					 | 
				
			||||||
        btn8 = findViewById(R.id.btn8);
 | 
					 | 
				
			||||||
        btn9 = findViewById(R.id.btn9);
 | 
					 | 
				
			||||||
        btn0 = findViewById(R.id.btn0);
 | 
					 | 
				
			||||||
        btn000 = findViewById(R.id.btn000);
 | 
					 | 
				
			||||||
        btnDelete = findViewById(R.id.btnDelete);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void setupSuccessScreen() {
 | 
					 | 
				
			||||||
        // Initially hide success screen
 | 
					 | 
				
			||||||
        if (successScreen != null) {
 | 
					 | 
				
			||||||
            successScreen.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void setupClickListeners() {
 | 
					 | 
				
			||||||
        // Back navigation - entire LinearLayout is clickable
 | 
					 | 
				
			||||||
        backNavigation.setOnClickListener(v -> {
 | 
					 | 
				
			||||||
            addClickAnimation(v);
 | 
					 | 
				
			||||||
            navigateBack();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Individual back arrow (for additional touch area)
 | 
					 | 
				
			||||||
        backArrow.setOnClickListener(v -> {
 | 
					 | 
				
			||||||
            addClickAnimation(v);
 | 
					 | 
				
			||||||
            navigateBack();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Toolbar title (also clickable for back navigation)
 | 
					 | 
				
			||||||
        toolbarTitle.setOnClickListener(v -> {
 | 
					 | 
				
			||||||
            addClickAnimation(v);
 | 
					 | 
				
			||||||
            navigateBack();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Numpad listeners
 | 
					 | 
				
			||||||
        btn1.setOnClickListener(v -> handleNumpadClick(v, "1"));
 | 
					 | 
				
			||||||
        btn2.setOnClickListener(v -> handleNumpadClick(v, "2"));
 | 
					 | 
				
			||||||
        btn3.setOnClickListener(v -> handleNumpadClick(v, "3"));
 | 
					 | 
				
			||||||
        btn4.setOnClickListener(v -> handleNumpadClick(v, "4"));
 | 
					 | 
				
			||||||
        btn5.setOnClickListener(v -> handleNumpadClick(v, "5"));
 | 
					 | 
				
			||||||
        btn6.setOnClickListener(v -> handleNumpadClick(v, "6"));
 | 
					 | 
				
			||||||
        btn7.setOnClickListener(v -> handleNumpadClick(v, "7"));
 | 
					 | 
				
			||||||
        btn8.setOnClickListener(v -> handleNumpadClick(v, "8"));
 | 
					 | 
				
			||||||
        btn9.setOnClickListener(v -> handleNumpadClick(v, "9"));
 | 
					 | 
				
			||||||
        btn0.setOnClickListener(v -> handleNumpadClick(v, "0"));
 | 
					 | 
				
			||||||
        btn000.setOnClickListener(v -> handleNumpadClick(v, "000"));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Delete button
 | 
					 | 
				
			||||||
        btnDelete.setOnClickListener(v -> {
 | 
					 | 
				
			||||||
            addClickAnimation(v);
 | 
					 | 
				
			||||||
            deleteLastDigit();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Confirm button
 | 
					 | 
				
			||||||
        confirmButton.setOnClickListener(v -> {
 | 
					 | 
				
			||||||
            if (confirmButton.isEnabled()) {
 | 
					 | 
				
			||||||
                addButtonClickAnimation(v);
 | 
					 | 
				
			||||||
                handleConfirmPin();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void navigateBack() {
 | 
					 | 
				
			||||||
        finish();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void handleNumpadClick(View view, String digit) {
 | 
					 | 
				
			||||||
        addClickAnimation(view);
 | 
					 | 
				
			||||||
        addDigit(digit);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void setupInitialStates() {
 | 
					 | 
				
			||||||
        // Set initial PIN display
 | 
					 | 
				
			||||||
        editTextPin.setText("");
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Set initial button state
 | 
					 | 
				
			||||||
        updateButtonState();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Disable EditText input (only numpad input allowed)
 | 
					 | 
				
			||||||
        editTextPin.setFocusable(false);
 | 
					 | 
				
			||||||
        editTextPin.setClickable(false);
 | 
					 | 
				
			||||||
        editTextPin.setCursorVisible(false);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void addDigit(String digit) {
 | 
					 | 
				
			||||||
        // Validate input length
 | 
					 | 
				
			||||||
        if (currentPin.length() >= MAX_PIN_LENGTH) {
 | 
					 | 
				
			||||||
            showToast("Maksimal " + MAX_PIN_LENGTH + " digit");
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Handle special case for 000
 | 
					 | 
				
			||||||
        if (digit.equals("000")) {
 | 
					 | 
				
			||||||
            if (currentPin.length() + 3 <= MAX_PIN_LENGTH) {
 | 
					 | 
				
			||||||
                currentPin.append("000");
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                int remainingLength = MAX_PIN_LENGTH - currentPin.length();
 | 
					 | 
				
			||||||
                if (remainingLength > 0) {
 | 
					 | 
				
			||||||
                    currentPin.append("0".repeat(remainingLength));
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            currentPin.append(digit);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        updatePinDisplay();
 | 
					 | 
				
			||||||
        updateButtonState();
 | 
					 | 
				
			||||||
        addInputFeedback();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void deleteLastDigit() {
 | 
					 | 
				
			||||||
        if (currentPin.length() > 0) {
 | 
					 | 
				
			||||||
            String current = currentPin.toString();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // If current ends with 000, remove all three digits
 | 
					 | 
				
			||||||
            if (current.endsWith("000") && current.length() >= 3) {
 | 
					 | 
				
			||||||
                currentPin.delete(currentPin.length() - 3, currentPin.length());
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                currentPin.deleteCharAt(currentPin.length() - 1);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            updatePinDisplay();
 | 
					 | 
				
			||||||
            updateButtonState();
 | 
					 | 
				
			||||||
            addDeleteFeedback();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void updatePinDisplay() {
 | 
					 | 
				
			||||||
        String pin = currentPin.toString();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        if (pin.isEmpty()) {
 | 
					 | 
				
			||||||
            editTextPin.setText("");
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            // Convert digits to asterisks for security
 | 
					 | 
				
			||||||
            String maskedPin = "*".repeat(pin.length());
 | 
					 | 
				
			||||||
            editTextPin.setText(maskedPin);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void updateButtonState() {
 | 
					 | 
				
			||||||
        boolean hasValidPin = currentPin.length() >= MIN_PIN_LENGTH;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        confirmButton.setEnabled(hasValidPin);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        if (hasValidPin) {
 | 
					 | 
				
			||||||
            // Active state
 | 
					 | 
				
			||||||
            confirmButton.setBackgroundResource(R.drawable.button_active_background);
 | 
					 | 
				
			||||||
            confirmButton.setTextColor(Color.WHITE);
 | 
					 | 
				
			||||||
            confirmButton.setAlpha(1.0f);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            // Inactive state
 | 
					 | 
				
			||||||
            confirmButton.setBackgroundResource(R.drawable.button_inactive_background);
 | 
					 | 
				
			||||||
            confirmButton.setTextColor(Color.parseColor("#999999"));
 | 
					 | 
				
			||||||
            confirmButton.setAlpha(0.6f);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void handleConfirmPin() {
 | 
					 | 
				
			||||||
        String pin = currentPin.toString();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        if (TextUtils.isEmpty(pin)) {
 | 
					 | 
				
			||||||
            showToast("Masukkan PIN");
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (pin.length() < MIN_PIN_LENGTH) {
 | 
					 | 
				
			||||||
            showToast("PIN minimal " + MIN_PIN_LENGTH + " digit");
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // Process PIN verification
 | 
					 | 
				
			||||||
        verifyPin(pin);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void verifyPin(String pin) {
 | 
					 | 
				
			||||||
        // Show loading state
 | 
					 | 
				
			||||||
        confirmButton.setText("Memverifikasi...");
 | 
					 | 
				
			||||||
        confirmButton.setEnabled(false);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Simulate PIN verification
 | 
					 | 
				
			||||||
        animationHandler.postDelayed(() -> {
 | 
					 | 
				
			||||||
            // For demo purposes, accept any PIN with length >= 4
 | 
					 | 
				
			||||||
            // In real implementation, this would call backend API
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (isValidPin(pin)) {
 | 
					 | 
				
			||||||
                // Show success screen instead of toast
 | 
					 | 
				
			||||||
                handleSuccessfulVerification();
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                showToast("PIN tidak valid. Silakan coba lagi.");
 | 
					 | 
				
			||||||
                resetPinState();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }, 2000);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private boolean isValidPin(String pin) {
 | 
					 | 
				
			||||||
        // Demo validation - in real app, this would validate against backend
 | 
					 | 
				
			||||||
        // For now, reject simple patterns like "1111", "1234", etc.
 | 
					 | 
				
			||||||
        return !pin.equals("1111") && 
 | 
					 | 
				
			||||||
               !pin.equals("1234") && 
 | 
					 | 
				
			||||||
               !pin.equals("0000") &&
 | 
					 | 
				
			||||||
               pin.length() >= MIN_PIN_LENGTH;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void handleSuccessfulVerification() {
 | 
					 | 
				
			||||||
        // Show full screen success message
 | 
					 | 
				
			||||||
        showSuccessScreen();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Navigate to receipt page after 2.5 seconds
 | 
					 | 
				
			||||||
        animationHandler.postDelayed(() -> {
 | 
					 | 
				
			||||||
            navigateToReceiptPage();
 | 
					 | 
				
			||||||
        }, 2500);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void showSuccessScreen() {
 | 
					 | 
				
			||||||
        if (successScreen != null) {
 | 
					 | 
				
			||||||
            // Hide all other UI components first
 | 
					 | 
				
			||||||
            hideMainUIComponents();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Set success message
 | 
					 | 
				
			||||||
            if (successMessage != null) {
 | 
					 | 
				
			||||||
                successMessage.setText("Pembayaran Berhasil");
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Show success screen with fade in animation
 | 
					 | 
				
			||||||
            successScreen.setVisibility(View.VISIBLE);
 | 
					 | 
				
			||||||
            successScreen.setAlpha(0f);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Fade in the background
 | 
					 | 
				
			||||||
            ObjectAnimator backgroundFadeIn = ObjectAnimator.ofFloat(successScreen, "alpha", 0f, 1f);
 | 
					 | 
				
			||||||
            backgroundFadeIn.setDuration(500);
 | 
					 | 
				
			||||||
            backgroundFadeIn.start();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Add scale and bounce animation to success icon
 | 
					 | 
				
			||||||
            if (successIcon != null) {
 | 
					 | 
				
			||||||
                // Start with invisible icon
 | 
					 | 
				
			||||||
                successIcon.setScaleX(0f);
 | 
					 | 
				
			||||||
                successIcon.setScaleY(0f);
 | 
					 | 
				
			||||||
                successIcon.setAlpha(0f);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                // Scale animation with bounce effect
 | 
					 | 
				
			||||||
                ObjectAnimator scaleX = ObjectAnimator.ofFloat(successIcon, "scaleX", 0f, 1.2f, 1f);
 | 
					 | 
				
			||||||
                ObjectAnimator scaleY = ObjectAnimator.ofFloat(successIcon, "scaleY", 0f, 1.2f, 1f);
 | 
					 | 
				
			||||||
                ObjectAnimator iconFadeIn = ObjectAnimator.ofFloat(successIcon, "alpha", 0f, 1f);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                AnimatorSet iconAnimation = new AnimatorSet();
 | 
					 | 
				
			||||||
                iconAnimation.playTogether(scaleX, scaleY, iconFadeIn);
 | 
					 | 
				
			||||||
                iconAnimation.setDuration(800);
 | 
					 | 
				
			||||||
                iconAnimation.setStartDelay(300);
 | 
					 | 
				
			||||||
                iconAnimation.setInterpolator(new android.view.animation.OvershootInterpolator(1.2f));
 | 
					 | 
				
			||||||
                iconAnimation.start();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // Add slide up animation to success message
 | 
					 | 
				
			||||||
            if (successMessage != null) {
 | 
					 | 
				
			||||||
                successMessage.setAlpha(0f);
 | 
					 | 
				
			||||||
                successMessage.setTranslationY(50f);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                ObjectAnimator messageSlideUp = ObjectAnimator.ofFloat(successMessage, "translationY", 50f, 0f);
 | 
					 | 
				
			||||||
                ObjectAnimator messageFadeIn = ObjectAnimator.ofFloat(successMessage, "alpha", 0f, 1f);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                AnimatorSet messageAnimation = new AnimatorSet();
 | 
					 | 
				
			||||||
                messageAnimation.playTogether(messageSlideUp, messageFadeIn);
 | 
					 | 
				
			||||||
                messageAnimation.setDuration(600);
 | 
					 | 
				
			||||||
                messageAnimation.setStartDelay(600);
 | 
					 | 
				
			||||||
                messageAnimation.setInterpolator(new android.view.animation.DecelerateInterpolator());
 | 
					 | 
				
			||||||
                messageAnimation.start();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void hideMainUIComponents() {
 | 
					 | 
				
			||||||
        // Hide all main UI components to create clean full screen success
 | 
					 | 
				
			||||||
        if (backNavigation != null) {
 | 
					 | 
				
			||||||
            backNavigation.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Hide the red header backgrounds
 | 
					 | 
				
			||||||
        View redStatusBar = findViewById(R.id.red_status_bar);
 | 
					 | 
				
			||||||
        View redHeaderBackground = findViewById(R.id.red_header_background);
 | 
					 | 
				
			||||||
        if (redStatusBar != null) {
 | 
					 | 
				
			||||||
            redStatusBar.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (redHeaderBackground != null) {
 | 
					 | 
				
			||||||
            redHeaderBackground.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Hide PIN card
 | 
					 | 
				
			||||||
        View pinCard = findViewById(R.id.pin_card);
 | 
					 | 
				
			||||||
        if (pinCard != null) {
 | 
					 | 
				
			||||||
            pinCard.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Hide numpad
 | 
					 | 
				
			||||||
        View numpadGrid = findViewById(R.id.numpad_grid);
 | 
					 | 
				
			||||||
        if (numpadGrid != null) {
 | 
					 | 
				
			||||||
            numpadGrid.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Hide confirm button
 | 
					 | 
				
			||||||
        if (confirmButton != null) {
 | 
					 | 
				
			||||||
            confirmButton.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void navigateToReceiptPage() {
 | 
					 | 
				
			||||||
        // Create intent to navigate to receipt/struk page
 | 
					 | 
				
			||||||
        Intent intent = new Intent(this, ReceiptActivity.class);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Pass transaction data
 | 
					 | 
				
			||||||
        intent.putExtra("transaction_amount", amount);
 | 
					 | 
				
			||||||
        intent.putExtra("pin_verified", true);
 | 
					 | 
				
			||||||
        intent.putExtra("source_activity", sourceActivity);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Add transaction details (you can customize these)
 | 
					 | 
				
			||||||
        intent.putExtra("merchant_name", "TOKO KLONTONG PAK EKO");
 | 
					 | 
				
			||||||
        intent.putExtra("merchant_location", "Ciputat Baru, Tangsel");
 | 
					 | 
				
			||||||
        intent.putExtra("transaction_id", generateTransactionId());
 | 
					 | 
				
			||||||
        intent.putExtra("transaction_date", getCurrentDateTime());
 | 
					 | 
				
			||||||
        intent.putExtra("payment_method", "Kartu Kredit");
 | 
					 | 
				
			||||||
        intent.putExtra("card_type", "BCA");
 | 
					 | 
				
			||||||
        intent.putExtra("tax_percentage", "11%");
 | 
					 | 
				
			||||||
        intent.putExtra("service_fee", "500");
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        startActivity(intent);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Set result for calling activity
 | 
					 | 
				
			||||||
        Intent resultIntent = new Intent();
 | 
					 | 
				
			||||||
        resultIntent.putExtra("pin_verified", true);
 | 
					 | 
				
			||||||
        resultIntent.putExtra("pin_length", currentPin.length());
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        if (!TextUtils.isEmpty(amount)) {
 | 
					 | 
				
			||||||
            resultIntent.putExtra(EXTRA_AMOUNT, amount);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        setResult(RESULT_OK, resultIntent);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Finish this activity
 | 
					 | 
				
			||||||
        finish();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private String generateTransactionId() {
 | 
					 | 
				
			||||||
        // Generate a simple transaction ID (in real app, this would come from backend)
 | 
					 | 
				
			||||||
        return String.valueOf(System.currentTimeMillis() % 1000000000L);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private String getCurrentDateTime() {
 | 
					 | 
				
			||||||
        // Get current date and time (in real app, use proper date formatting)
 | 
					 | 
				
			||||||
        java.text.SimpleDateFormat sdf = new java.text.SimpleDateFormat("dd MMMM yyyy HH:mm", java.util.Locale.getDefault());
 | 
					 | 
				
			||||||
        return sdf.format(new java.util.Date());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void resetPinState() {
 | 
					 | 
				
			||||||
        currentPin = new StringBuilder();
 | 
					 | 
				
			||||||
        updatePinDisplay();
 | 
					 | 
				
			||||||
        updateButtonState();
 | 
					 | 
				
			||||||
        confirmButton.setText("Konfirmasi");
 | 
					 | 
				
			||||||
        confirmButton.setEnabled(false);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Animation methods
 | 
					 | 
				
			||||||
    private void addClickAnimation(View view) {
 | 
					 | 
				
			||||||
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.95f, 1f);
 | 
					 | 
				
			||||||
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.95f, 1f);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        AnimatorSet animatorSet = new AnimatorSet();
 | 
					 | 
				
			||||||
        animatorSet.playTogether(scaleX, scaleY);
 | 
					 | 
				
			||||||
        animatorSet.setDuration(150);
 | 
					 | 
				
			||||||
        animatorSet.start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void addButtonClickAnimation(View view) {
 | 
					 | 
				
			||||||
        ObjectAnimator scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.98f, 1f);
 | 
					 | 
				
			||||||
        ObjectAnimator scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.98f, 1f);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        AnimatorSet animatorSet = new AnimatorSet();
 | 
					 | 
				
			||||||
        animatorSet.playTogether(scaleX, scaleY);
 | 
					 | 
				
			||||||
        animatorSet.setDuration(200);
 | 
					 | 
				
			||||||
        animatorSet.start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void addInputFeedback() {
 | 
					 | 
				
			||||||
        ObjectAnimator fadeIn = ObjectAnimator.ofFloat(editTextPin, "alpha", 0.7f, 1f);
 | 
					 | 
				
			||||||
        fadeIn.setDuration(200);
 | 
					 | 
				
			||||||
        fadeIn.start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void addDeleteFeedback() {
 | 
					 | 
				
			||||||
        ObjectAnimator shake = ObjectAnimator.ofFloat(editTextPin, "translationX", 0f, -10f, 10f, 0f);
 | 
					 | 
				
			||||||
        shake.setDuration(300);
 | 
					 | 
				
			||||||
        shake.start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Utility methods
 | 
					 | 
				
			||||||
    private void showToast(String message) {
 | 
					 | 
				
			||||||
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void onBackPressed() {
 | 
					 | 
				
			||||||
        // Prevent back press when success screen is showing
 | 
					 | 
				
			||||||
        if (successScreen != null && successScreen.getVisibility() == View.VISIBLE) {
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        navigateBack();
 | 
					 | 
				
			||||||
        super.onBackPressed();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void onDestroy() {
 | 
					 | 
				
			||||||
        super.onDestroy();
 | 
					 | 
				
			||||||
        if (animationHandler != null) {
 | 
					 | 
				
			||||||
            animationHandler.removeCallbacksAndMessages(null);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Public methods for testing
 | 
					 | 
				
			||||||
    public String getCurrentPin() {
 | 
					 | 
				
			||||||
        return currentPin.toString();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public boolean isConfirmButtonEnabled() {
 | 
					 | 
				
			||||||
        return confirmButton.isEnabled();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Static helper method to launch PinActivity
 | 
					 | 
				
			||||||
    public static void launch(android.content.Context context, String sourceActivity, String amount) {
 | 
					 | 
				
			||||||
        Intent intent = new Intent(context, PinActivity.class);
 | 
					 | 
				
			||||||
        intent.putExtra(EXTRA_SOURCE_ACTIVITY, sourceActivity);
 | 
					 | 
				
			||||||
        if (!TextUtils.isEmpty(amount)) {
 | 
					 | 
				
			||||||
            intent.putExtra(EXTRA_AMOUNT, amount);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        // Launch for result if context is an Activity
 | 
					 | 
				
			||||||
        if (context instanceof AppCompatActivity) {
 | 
					 | 
				
			||||||
            ((AppCompatActivity) context).startActivityForResult(intent, 100);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            context.startActivity(intent);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -24,6 +24,8 @@ import java.io.OutputStream;
 | 
				
			|||||||
import java.net.URL;
 | 
					import java.net.URL;
 | 
				
			||||||
import java.net.URI;
 | 
					import java.net.URI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.cetakulang.ReprintActivity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ReceiptActivity extends AppCompatActivity {
 | 
					public class ReceiptActivity extends AppCompatActivity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Views
 | 
					    // Views
 | 
				
			||||||
@ -902,9 +904,9 @@ public class ReceiptActivity extends AppCompatActivity {
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        if (callingActivity != null) {
 | 
					        if (callingActivity != null) {
 | 
				
			||||||
            switch (callingActivity) {
 | 
					            switch (callingActivity) {
 | 
				
			||||||
                case "TransactionActivity":
 | 
					                case "ReprintActivity":
 | 
				
			||||||
                    // Go back to transaction list
 | 
					                    // Go back to transaction list
 | 
				
			||||||
                    Intent transactionIntent = new Intent(this, TransactionActivity.class);
 | 
					                    Intent transactionIntent = new Intent(this, ReprintActivity.class);
 | 
				
			||||||
                    transactionIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
 | 
					                    transactionIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
 | 
				
			||||||
                    startActivity(transactionIntent);
 | 
					                    startActivity(transactionIntent);
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
package com.example.bdkipoc;
 | 
					package com.example.bdkipoc.cetakulang;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.R;
 | 
				
			||||||
import android.content.SharedPreferences;
 | 
					import android.content.SharedPreferences;
 | 
				
			||||||
import android.os.AsyncTask;
 | 
					import android.os.AsyncTask;
 | 
				
			||||||
import android.os.Bundle;
 | 
					import android.os.Bundle;
 | 
				
			||||||
@ -50,9 +51,12 @@ import java.util.TimeZone;
 | 
				
			|||||||
import android.app.DatePickerDialog;
 | 
					import android.app.DatePickerDialog;
 | 
				
			||||||
import android.widget.DatePicker;
 | 
					import android.widget.DatePicker;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class TransactionActivity extends AppCompatActivity implements TransactionAdapter.OnPrintClickListener {
 | 
					import com.example.bdkipoc.ReceiptActivity;
 | 
				
			||||||
 | 
					import com.example.bdkipoc.StyleHelper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ReprintActivity extends AppCompatActivity implements ReprintAdapterActivity.OnPrintClickListener {
 | 
				
			||||||
    private RecyclerView recyclerView;
 | 
					    private RecyclerView recyclerView;
 | 
				
			||||||
    private TransactionAdapter adapter;
 | 
					    private ReprintAdapterActivity adapter;
 | 
				
			||||||
    private List<Transaction> transactionList;
 | 
					    private List<Transaction> transactionList;
 | 
				
			||||||
    private List<Transaction> filteredList;
 | 
					    private List<Transaction> filteredList;
 | 
				
			||||||
    private ProgressBar progressBar;
 | 
					    private ProgressBar progressBar;
 | 
				
			||||||
@ -89,7 +93,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void onCreate(@Nullable Bundle savedInstanceState) {
 | 
					    protected void onCreate(@Nullable Bundle savedInstanceState) {
 | 
				
			||||||
        super.onCreate(savedInstanceState);
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
        setContentView(R.layout.activity_transaction);
 | 
					        setContentView(R.layout.activity_reprint);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // ✅ Initialize SharedPreferences for local tracking
 | 
					        // ✅ Initialize SharedPreferences for local tracking
 | 
				
			||||||
        prefs = getSharedPreferences("transaction_prefs", MODE_PRIVATE);
 | 
					        prefs = getSharedPreferences("transaction_prefs", MODE_PRIVATE);
 | 
				
			||||||
@ -159,7 +163,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
        transactionList = new ArrayList<>();
 | 
					        transactionList = new ArrayList<>();
 | 
				
			||||||
        filteredList = new ArrayList<>();
 | 
					        filteredList = new ArrayList<>();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        adapter = new TransactionAdapter(filteredList);
 | 
					        adapter = new ReprintAdapterActivity(filteredList);
 | 
				
			||||||
        adapter.setPrintClickListener(this);
 | 
					        adapter.setPrintClickListener(this);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
 | 
					        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
 | 
				
			||||||
@ -342,13 +346,13 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            filterButtonText.setTextColor(getResources().getColor(android.R.color.holo_blue_dark));
 | 
					            filterButtonText.setTextColor(getResources().getColor(android.R.color.holo_blue_dark));
 | 
				
			||||||
            filterButtonText.setTextSize(12); // Smaller text when filter is active
 | 
					            filterButtonText.setTextSize(12); // Smaller text when filter is active
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Log.d("TransactionActivity", "🎨 Filter button updated: " + displayText);
 | 
					            Log.d("ReprintActivity", "🎨 Filter button updated: " + displayText);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // ✅ NEW METHOD: Apply date filter
 | 
					    // ✅ NEW METHOD: Apply date filter
 | 
				
			||||||
    private void applyDateFilter() {
 | 
					    private void applyDateFilter() {
 | 
				
			||||||
        Log.d("TransactionActivity", "🗓️ Applying date filter: " + fromDate + " to " + toDate);
 | 
					        Log.d("ReprintActivity", "🗓️ Applying date filter: " + fromDate + " to " + toDate);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Reset to first page and reload data
 | 
					        // Reset to first page and reload data
 | 
				
			||||||
        currentPage = 1;
 | 
					        currentPage = 1;
 | 
				
			||||||
@ -366,7 +370,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            filterButtonText.setTextSize(14); // Reset to normal size
 | 
					            filterButtonText.setTextSize(14); // Reset to normal size
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionActivity", "🗓️ Date filter cleared");
 | 
					        Log.d("ReprintActivity", "🗓️ Date filter cleared");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Reload data without date filter
 | 
					        // Reload data without date filter
 | 
				
			||||||
        currentPage = 1;
 | 
					        currentPage = 1;
 | 
				
			||||||
@ -377,7 +381,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            return;
 | 
					            return;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionActivity", "🔄 Navigating to page " + page);
 | 
					        Log.d("ReprintActivity", "🔄 Navigating to page " + page);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if (currentSearchQuery.isEmpty()) {
 | 
					        if (currentSearchQuery.isEmpty()) {
 | 
				
			||||||
            // Load from API
 | 
					            // Load from API
 | 
				
			||||||
@ -410,7 +414,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
        // Scroll to top
 | 
					        // Scroll to top
 | 
				
			||||||
        recyclerView.scrollToPosition(0);
 | 
					        recyclerView.scrollToPosition(0);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionActivity", "📄 Displaying search results page " + currentPage + 
 | 
					        Log.d("ReprintActivity", "📄 Displaying search results page " + currentPage + 
 | 
				
			||||||
            " (items " + (startIndex + 1) + "-" + endIndex + " of " + filteredList.size() + ")");
 | 
					            " (items " + (startIndex + 1) + "-" + endIndex + " of " + filteredList.size() + ")");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -452,10 +456,10 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            totalPages = (int) Math.ceil((double) totalRecords / itemsPerPage);
 | 
					            totalPages = (int) Math.ceil((double) totalRecords / itemsPerPage);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // ✅ PASTIKAN TIDAK PERLU SORT LAGI karena sudah sorted dari API response
 | 
					            // ✅ PASTIKAN TIDAK PERLU SORT LAGI karena sudah sorted dari API response
 | 
				
			||||||
            Log.d("TransactionActivity", "📋 FILTERED LIST ORDER (no search - maintaining API order):");
 | 
					            Log.d("ReprintActivity", "📋 FILTERED LIST ORDER (no search - maintaining API order):");
 | 
				
			||||||
            for (int i = 0; i < Math.min(5, filteredList.size()); i++) {
 | 
					            for (int i = 0; i < Math.min(5, filteredList.size()); i++) {
 | 
				
			||||||
                Transaction tx = filteredList.get(i);
 | 
					                Transaction tx = filteredList.get(i);
 | 
				
			||||||
                Log.d("TransactionActivity", "   " + (i+1) + ". " + tx.createdAt + " - " + tx.referenceId);
 | 
					                Log.d("ReprintActivity", "   " + (i+1) + ". " + tx.createdAt + " - " + tx.referenceId);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // ✅ SEARCH MODE: Filter all available data
 | 
					            // ✅ SEARCH MODE: Filter all available data
 | 
				
			||||||
@ -518,7 +522,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            paginationControls.setVisibility(View.GONE);
 | 
					            paginationControls.setVisibility(View.GONE);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionActivity", "📊 Pagination updated: " + 
 | 
					        Log.d("ReprintActivity", "📊 Pagination updated: " + 
 | 
				
			||||||
            "Page " + currentPage + "/" + totalPages + ", Total: " + totalRecords);
 | 
					            "Page " + currentPage + "/" + totalPages + ", Total: " + totalRecords);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -584,7 +588,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            pageNumbersContainer.addView(pageButton);
 | 
					            pageNumbersContainer.addView(pageButton);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionActivity", "🔢 Page buttons created: " + startPage + " to " + endPage + 
 | 
					        Log.d("ReprintActivity", "🔢 Page buttons created: " + startPage + " to " + endPage + 
 | 
				
			||||||
            " with size: " + buttonSize + "px");
 | 
					            " with size: " + buttonSize + "px");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -613,7 +617,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
                String urlString = "https://be-edc.msvc.app/transactions?page=" + apiPage + 
 | 
					                String urlString = "https://be-edc.msvc.app/transactions?page=" + apiPage + 
 | 
				
			||||||
                    "&limit=" + itemsPerPage + "&sortOrder=DESC&from_date=" + fromDate + "&to_date=" + toDate + "&location_id=0&merchant_id=0&tid=73001500&mid=71000026521&sortColumn=created_at";
 | 
					                    "&limit=" + itemsPerPage + "&sortOrder=DESC&from_date=" + fromDate + "&to_date=" + toDate + "&location_id=0&merchant_id=0&tid=73001500&mid=71000026521&sortColumn=created_at";
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                Log.d("TransactionActivity", "🔍 Fetching transactions page " + pageToLoad + 
 | 
					                Log.d("ReprintActivity", "🔍 Fetching transactions page " + pageToLoad + 
 | 
				
			||||||
                    " (API page " + apiPage + ") with limit " + itemsPerPage + " - SORT: DESC by created_at" +
 | 
					                    " (API page " + apiPage + ") with limit " + itemsPerPage + " - SORT: DESC by created_at" +
 | 
				
			||||||
                    " - Date Filter: " + fromDate + " to " + toDate);
 | 
					                    " - Date Filter: " + fromDate + " to " + toDate);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
@ -642,7 +646,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
                    apiTotal = results.getInt("total");
 | 
					                    apiTotal = results.getInt("total");
 | 
				
			||||||
                    JSONArray data = results.getJSONArray("data");
 | 
					                    JSONArray data = results.getJSONArray("data");
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    Log.d("TransactionActivity", "📊 API response: " + data.length() + 
 | 
					                    Log.d("ReprintActivity", "📊 API response: " + data.length() + 
 | 
				
			||||||
                        " records, total: " + apiTotal);
 | 
					                        " records, total: " + apiTotal);
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    // ✅ STEP 1: Parse all transactions from API
 | 
					                    // ✅ STEP 1: Parse all transactions from API
 | 
				
			||||||
@ -667,14 +671,14 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
                    // ✅ STEP 2: Apply intelligent deduplication
 | 
					                    // ✅ STEP 2: Apply intelligent deduplication
 | 
				
			||||||
                    result = applyAdvancedDeduplication(rawTransactions);
 | 
					                    result = applyAdvancedDeduplication(rawTransactions);
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    Log.d("TransactionActivity", "✅ After advanced deduplication: " + result.size() + " unique transactions");
 | 
					                    Log.d("ReprintActivity", "✅ After advanced deduplication: " + result.size() + " unique transactions");
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    Log.e("TransactionActivity", "❌ HTTP Error: " + responseCode);
 | 
					                    Log.e("ReprintActivity", "❌ HTTP Error: " + responseCode);
 | 
				
			||||||
                    error = true;
 | 
					                    error = true;
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } catch (IOException | JSONException | URISyntaxException e) {
 | 
					            } catch (IOException | JSONException | URISyntaxException e) {
 | 
				
			||||||
                Log.e("TransactionActivity", "❌ Exception: " + e.getMessage(), e);
 | 
					                Log.e("ReprintActivity", "❌ Exception: " + e.getMessage(), e);
 | 
				
			||||||
                error = true;
 | 
					                error = true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            return result;
 | 
					            return result;
 | 
				
			||||||
@ -687,7 +691,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            progressBar.setVisibility(View.GONE);
 | 
					            progressBar.setVisibility(View.GONE);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            if (error) {
 | 
					            if (error) {
 | 
				
			||||||
                Toast.makeText(TransactionActivity.this, "Failed to fetch transactions", Toast.LENGTH_SHORT).show();
 | 
					                Toast.makeText(ReprintActivity.this, "Failed to fetch transactions", Toast.LENGTH_SHORT).show();
 | 
				
			||||||
                updatePaginationDisplay();
 | 
					                updatePaginationDisplay();
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -705,11 +709,11 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
                    
 | 
					                    
 | 
				
			||||||
                    if (date1 != null && date2 != null) {
 | 
					                    if (date1 != null && date2 != null) {
 | 
				
			||||||
                        int comparison = date2.compareTo(date1); // Newest first
 | 
					                        int comparison = date2.compareTo(date1); // Newest first
 | 
				
			||||||
                        Log.d("TransactionActivity", "🔄 Sorting: " + t2.createdAt + " vs " + t1.createdAt + " = " + comparison);
 | 
					                        Log.d("ReprintActivity", "🔄 Sorting: " + t2.createdAt + " vs " + t1.createdAt + " = " + comparison);
 | 
				
			||||||
                        return comparison;
 | 
					                        return comparison;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                } catch (Exception e) {
 | 
					                } catch (Exception e) {
 | 
				
			||||||
                    Log.w("TransactionActivity", "Date comparison error: " + e.getMessage());
 | 
					                    Log.w("ReprintActivity", "Date comparison error: " + e.getMessage());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                return Integer.compare(t2.id, t1.id); // Fallback by ID (higher ID = newer)
 | 
					                return Integer.compare(t2.id, t1.id); // Fallback by ID (higher ID = newer)
 | 
				
			||||||
            });
 | 
					            });
 | 
				
			||||||
@ -718,14 +722,14 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            transactionList.clear();
 | 
					            transactionList.clear();
 | 
				
			||||||
            transactionList.addAll(transactions);
 | 
					            transactionList.addAll(transactions);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Log.d("TransactionActivity", "📋 Page " + currentPage + " loaded and sorted: " + 
 | 
					            Log.d("ReprintActivity", "📋 Page " + currentPage + " loaded and sorted: " + 
 | 
				
			||||||
                transactions.size() + " transactions. Total: " + totalRecords + "/" + totalPages + " pages");
 | 
					                transactions.size() + " transactions. Total: " + totalRecords + "/" + totalPages + " pages");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // ✅ VERIFIKASI SORTING ORDER
 | 
					            // ✅ VERIFIKASI SORTING ORDER
 | 
				
			||||||
            Log.d("TransactionActivity", "📋 SORTED ORDER VERIFICATION:");
 | 
					            Log.d("ReprintActivity", "📋 SORTED ORDER VERIFICATION:");
 | 
				
			||||||
            for (int i = 0; i < Math.min(5, transactionList.size()); i++) {
 | 
					            for (int i = 0; i < Math.min(5, transactionList.size()); i++) {
 | 
				
			||||||
                Transaction tx = transactionList.get(i);
 | 
					                Transaction tx = transactionList.get(i);
 | 
				
			||||||
                Log.d("TransactionActivity", "   " + (i+1) + ". " + tx.createdAt + " - " + tx.referenceId);
 | 
					                Log.d("ReprintActivity", "   " + (i+1) + ". " + tx.createdAt + " - " + tx.referenceId);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Update filtered list based on current search
 | 
					            // Update filtered list based on current search
 | 
				
			||||||
@ -761,7 +765,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
                SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault());
 | 
					                SimpleDateFormat sdf = new SimpleDateFormat(format, Locale.getDefault());
 | 
				
			||||||
                sdf.setTimeZone(TimeZone.getTimeZone("UTC")); // Handle timezone properly
 | 
					                sdf.setTimeZone(TimeZone.getTimeZone("UTC")); // Handle timezone properly
 | 
				
			||||||
                Date parsed = sdf.parse(rawDate);
 | 
					                Date parsed = sdf.parse(rawDate);
 | 
				
			||||||
                Log.d("TransactionActivity", "✅ Date parsed successfully: " + rawDate + " -> " + parsed + " using format: " + format);
 | 
					                Log.d("ReprintActivity", "✅ Date parsed successfully: " + rawDate + " -> " + parsed + " using format: " + format);
 | 
				
			||||||
                return parsed;
 | 
					                return parsed;
 | 
				
			||||||
            } catch (Exception e) {
 | 
					            } catch (Exception e) {
 | 
				
			||||||
                // Continue to next format
 | 
					                // Continue to next format
 | 
				
			||||||
@ -779,10 +783,10 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            
 | 
					            
 | 
				
			||||||
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
					            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
				
			||||||
            Date parsed = sdf.parse(cleanedDate);
 | 
					            Date parsed = sdf.parse(cleanedDate);
 | 
				
			||||||
            Log.d("TransactionActivity", "✅ Date parsed with fallback: " + rawDate + " -> " + parsed);
 | 
					            Log.d("ReprintActivity", "✅ Date parsed with fallback: " + rawDate + " -> " + parsed);
 | 
				
			||||||
            return parsed;
 | 
					            return parsed;
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } catch (Exception e) {
 | 
				
			||||||
            Log.w("TransactionActivity", "❌ Could not parse date: " + rawDate + " - Error: " + e.getMessage());
 | 
					            Log.w("ReprintActivity", "❌ Could not parse date: " + rawDate + " - Error: " + e.getMessage());
 | 
				
			||||||
            return null;
 | 
					            return null;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -791,11 +795,11 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
     * ✅ ADVANCED DEDUPLICATION: Enhanced algorithm with multiple strategies
 | 
					     * ✅ ADVANCED DEDUPLICATION: Enhanced algorithm with multiple strategies
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private List<Transaction> applyAdvancedDeduplication(List<Transaction> rawTransactions) {
 | 
					    private List<Transaction> applyAdvancedDeduplication(List<Transaction> rawTransactions) {
 | 
				
			||||||
        Log.d("TransactionActivity", "🧠 Starting advanced deduplication...");
 | 
					        Log.d("ReprintActivity", "🧠 Starting advanced deduplication...");
 | 
				
			||||||
        Log.d("TransactionActivity", "📥 Input transactions order (first 5):");
 | 
					        Log.d("ReprintActivity", "📥 Input transactions order (first 5):");
 | 
				
			||||||
        for (int i = 0; i < Math.min(5, rawTransactions.size()); i++) {
 | 
					        for (int i = 0; i < Math.min(5, rawTransactions.size()); i++) {
 | 
				
			||||||
            Transaction tx = rawTransactions.get(i);
 | 
					            Transaction tx = rawTransactions.get(i);
 | 
				
			||||||
            Log.d("TransactionActivity", "   " + (i+1) + ". ID:" + tx.id + " Date:" + tx.createdAt + " Ref:" + tx.referenceId);
 | 
					            Log.d("ReprintActivity", "   " + (i+1) + ". ID:" + tx.id + " Date:" + tx.createdAt + " Ref:" + tx.referenceId);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Strategy 1: Group by reference_id
 | 
					        // Strategy 1: Group by reference_id
 | 
				
			||||||
@ -823,7 +827,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            if (group.size() == 1) {
 | 
					            if (group.size() == 1) {
 | 
				
			||||||
                // No duplicates for this reference
 | 
					                // No duplicates for this reference
 | 
				
			||||||
                deduplicatedList.add(group.get(0));
 | 
					                deduplicatedList.add(group.get(0));
 | 
				
			||||||
                Log.d("TransactionActivity", "✅ Unique transaction: " + referenceId);
 | 
					                Log.d("ReprintActivity", "✅ Unique transaction: " + referenceId);
 | 
				
			||||||
            } else {
 | 
					            } else {
 | 
				
			||||||
                // Multiple transactions with same reference_id - sort group by date first
 | 
					                // Multiple transactions with same reference_id - sort group by date first
 | 
				
			||||||
                Collections.sort(group, (t1, t2) -> {
 | 
					                Collections.sort(group, (t1, t2) -> {
 | 
				
			||||||
@ -843,15 +847,15 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
                deduplicatedList.add(bestTransaction);
 | 
					                deduplicatedList.add(bestTransaction);
 | 
				
			||||||
                duplicatesRemoved += (group.size() - 1);
 | 
					                duplicatesRemoved += (group.size() - 1);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                Log.d("TransactionActivity", "🔄 Deduplicated " + group.size() + " → 1 for ref: " + referenceId + 
 | 
					                Log.d("ReprintActivity", "🔄 Deduplicated " + group.size() + " → 1 for ref: " + referenceId + 
 | 
				
			||||||
                    " (kept ID: " + bestTransaction.id + ", status: " + bestTransaction.status + ", date: " + bestTransaction.createdAt + ")");
 | 
					                    " (kept ID: " + bestTransaction.id + ", status: " + bestTransaction.status + ", date: " + bestTransaction.createdAt + ")");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionActivity", "✅ Advanced deduplication complete:");
 | 
					        Log.d("ReprintActivity", "✅ Advanced deduplication complete:");
 | 
				
			||||||
        Log.d("TransactionActivity", "   📥 Input: " + rawTransactions.size() + " transactions");
 | 
					        Log.d("ReprintActivity", "   📥 Input: " + rawTransactions.size() + " transactions");
 | 
				
			||||||
        Log.d("TransactionActivity", "   📤 Output: " + deduplicatedList.size() + " unique transactions");
 | 
					        Log.d("ReprintActivity", "   📤 Output: " + deduplicatedList.size() + " unique transactions");
 | 
				
			||||||
        Log.d("TransactionActivity", "   🗑️ Removed: " + duplicatesRemoved + " duplicates");
 | 
					        Log.d("ReprintActivity", "   🗑️ Removed: " + duplicatesRemoved + " duplicates");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        return deduplicatedList;
 | 
					        return deduplicatedList;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -860,7 +864,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
     * ✅ ENHANCED SELECTION: Advanced algorithm to pick the best transaction
 | 
					     * ✅ ENHANCED SELECTION: Advanced algorithm to pick the best transaction
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    private Transaction selectBestTransactionAdvanced(List<Transaction> duplicates, String referenceId) {
 | 
					    private Transaction selectBestTransactionAdvanced(List<Transaction> duplicates, String referenceId) {
 | 
				
			||||||
        Log.d("TransactionActivity", "🎯 Selecting best from " + duplicates.size() + " duplicates for: " + referenceId);
 | 
					        Log.d("ReprintActivity", "🎯 Selecting best from " + duplicates.size() + " duplicates for: " + referenceId);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Transaction bestTransaction = duplicates.get(0);
 | 
					        Transaction bestTransaction = duplicates.get(0);
 | 
				
			||||||
        int bestPriority = getStatusPriority(bestTransaction.status);
 | 
					        int bestPriority = getStatusPriority(bestTransaction.status);
 | 
				
			||||||
@ -871,7 +875,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            int currentPriority = getStatusPriority(tx.status);
 | 
					            int currentPriority = getStatusPriority(tx.status);
 | 
				
			||||||
            Date currentDate = parseCreatedAtDate(tx.createdAt);
 | 
					            Date currentDate = parseCreatedAtDate(tx.createdAt);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Log.d("TransactionActivity", "   📊 Candidate: ID=" + tx.id + 
 | 
					            Log.d("ReprintActivity", "   📊 Candidate: ID=" + tx.id + 
 | 
				
			||||||
                ", Status=" + tx.status + " (priority=" + currentPriority + ")" +
 | 
					                ", Status=" + tx.status + " (priority=" + currentPriority + ")" +
 | 
				
			||||||
                ", Created=" + tx.createdAt);
 | 
					                ", Created=" + tx.createdAt);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
@ -903,11 +907,11 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
                bestTransaction = tx;
 | 
					                bestTransaction = tx;
 | 
				
			||||||
                bestPriority = currentPriority;
 | 
					                bestPriority = currentPriority;
 | 
				
			||||||
                bestDate = currentDate;
 | 
					                bestDate = currentDate;
 | 
				
			||||||
                Log.d("TransactionActivity", "   ⭐ NEW BEST selected: " + reason);
 | 
					                Log.d("ReprintActivity", "   ⭐ NEW BEST selected: " + reason);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionActivity", "🏆 FINAL SELECTION: ID=" + bestTransaction.id + 
 | 
					        Log.d("ReprintActivity", "🏆 FINAL SELECTION: ID=" + bestTransaction.id + 
 | 
				
			||||||
            ", Status=" + bestTransaction.status + ", Created=" + bestTransaction.createdAt);
 | 
					            ", Status=" + bestTransaction.status + ", Created=" + bestTransaction.createdAt);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        return bestTransaction;
 | 
					        return bestTransaction;
 | 
				
			||||||
@ -925,7 +929,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
                return date1.after(date2);
 | 
					                return date1.after(date2);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } catch (Exception e) {
 | 
				
			||||||
            Log.w("TransactionActivity", "Date comparison error, falling back to ID comparison");
 | 
					            Log.w("ReprintActivity", "Date comparison error, falling back to ID comparison");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Fallback: higher ID usually means newer
 | 
					        // Fallback: higher ID usually means newer
 | 
				
			||||||
@ -990,7 +994,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
                
 | 
					                
 | 
				
			||||||
            // Tier 5: Unknown status
 | 
					            // Tier 5: Unknown status
 | 
				
			||||||
            default:
 | 
					            default:
 | 
				
			||||||
                Log.w("TransactionActivity", "Unknown status encountered: " + status);
 | 
					                Log.w("ReprintActivity", "Unknown status encountered: " + status);
 | 
				
			||||||
                return 1;
 | 
					                return 1;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -1002,12 +1006,12 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
        Intent intent = new Intent(this, ReceiptActivity.class);
 | 
					        Intent intent = new Intent(this, ReceiptActivity.class);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Add calling activity information for proper back navigation
 | 
					        // Add calling activity information for proper back navigation
 | 
				
			||||||
        intent.putExtra("calling_activity", "TransactionActivity");
 | 
					        intent.putExtra("calling_activity", "ReprintActivity");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Extract and send raw amount properly
 | 
					        // Extract and send raw amount properly
 | 
				
			||||||
        String rawAmount = extractRawAmount(transaction.amount);
 | 
					        String rawAmount = extractRawAmount(transaction.amount);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionActivity", "Opening receipt for transaction: " + transaction.referenceId + 
 | 
					        Log.d("ReprintActivity", "Opening receipt for transaction: " + transaction.referenceId + 
 | 
				
			||||||
            ", channel: " + transaction.channelCode + ", original amount: '" + transaction.amount + "'");
 | 
					            ", channel: " + transaction.channelCode + ", original amount: '" + transaction.amount + "'");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Send transaction data to ReceiptActivity
 | 
					        // Send transaction data to ReceiptActivity
 | 
				
			||||||
@ -1032,7 +1036,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
        String acquirer = getRealAcquirerForQris(transaction.referenceId, transaction.channelCode);
 | 
					        String acquirer = getRealAcquirerForQris(transaction.referenceId, transaction.channelCode);
 | 
				
			||||||
        intent.putExtra("acquirer", acquirer); // Jenis Kartu
 | 
					        intent.putExtra("acquirer", acquirer); // Jenis Kartu
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionActivity", "🎯 Determined acquirer: " + acquirer + " for channel: " + transaction.channelCode);
 | 
					        Log.d("ReprintActivity", "🎯 Determined acquirer: " + acquirer + " for channel: " + transaction.channelCode);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        startActivity(intent);
 | 
					        startActivity(intent);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -1084,7 +1088,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
        
 | 
					        
 | 
				
			||||||
        // For QRIS, we could implement real-time acquirer lookup here
 | 
					        // For QRIS, we could implement real-time acquirer lookup here
 | 
				
			||||||
        // For now, return "qris" and let ReceiptActivity handle the detection
 | 
					        // For now, return "qris" and let ReceiptActivity handle the detection
 | 
				
			||||||
        Log.d("TransactionActivity", "🔍 QRIS transaction detected, deferring acquirer detection to ReceiptActivity");
 | 
					        Log.d("ReprintActivity", "🔍 QRIS transaction detected, deferring acquirer detection to ReceiptActivity");
 | 
				
			||||||
        return "qris";
 | 
					        return "qris";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1128,7 +1132,7 @@ public class TransactionActivity extends AppCompatActivity implements Transactio
 | 
				
			|||||||
            Long.parseLong(cleaned);
 | 
					            Long.parseLong(cleaned);
 | 
				
			||||||
            return cleaned;
 | 
					            return cleaned;
 | 
				
			||||||
        } catch (NumberFormatException e) {
 | 
					        } catch (NumberFormatException e) {
 | 
				
			||||||
            Log.e("TransactionActivity", "Invalid amount: " + formattedAmount);
 | 
					            Log.e("ReprintActivity", "Invalid amount: " + formattedAmount);
 | 
				
			||||||
            return "0";
 | 
					            return "0";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
package com.example.bdkipoc;
 | 
					package com.example.bdkipoc.cetakulang;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.R;
 | 
				
			||||||
import android.util.Log;
 | 
					import android.util.Log;
 | 
				
			||||||
import android.view.LayoutInflater;
 | 
					import android.view.LayoutInflater;
 | 
				
			||||||
import android.view.View;
 | 
					import android.view.View;
 | 
				
			||||||
@ -26,15 +27,17 @@ import org.json.JSONArray;
 | 
				
			|||||||
import org.json.JSONException;
 | 
					import org.json.JSONException;
 | 
				
			||||||
import org.json.JSONObject;
 | 
					import org.json.JSONObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.TransactionViewHolder> {
 | 
					import com.example.bdkipoc.StyleHelper;
 | 
				
			||||||
    private List<TransactionActivity.Transaction> transactionList;
 | 
					
 | 
				
			||||||
 | 
					public class ReprintAdapterActivity extends RecyclerView.Adapter<ReprintAdapterActivity.TransactionViewHolder> {
 | 
				
			||||||
 | 
					    private List<ReprintActivity.Transaction> transactionList;
 | 
				
			||||||
    private OnPrintClickListener printClickListener;
 | 
					    private OnPrintClickListener printClickListener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public interface OnPrintClickListener {
 | 
					    public interface OnPrintClickListener {
 | 
				
			||||||
        void onPrintClick(TransactionActivity.Transaction transaction);
 | 
					        void onPrintClick(ReprintActivity.Transaction transaction);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public TransactionAdapter(List<TransactionActivity.Transaction> transactionList) {
 | 
					    public ReprintAdapterActivity(List<ReprintActivity.Transaction> transactionList) {
 | 
				
			||||||
        this.transactionList = transactionList;
 | 
					        this.transactionList = transactionList;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -45,23 +48,23 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Update data without numbering (removed as per request)
 | 
					     * Update data without numbering (removed as per request)
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public void updateData(List<TransactionActivity.Transaction> newData, int startIndex) {
 | 
					    public void updateData(List<ReprintActivity.Transaction> newData, int startIndex) {
 | 
				
			||||||
        this.transactionList = newData;
 | 
					        this.transactionList = newData;
 | 
				
			||||||
        notifyDataSetChanged();
 | 
					        notifyDataSetChanged();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionAdapter", "📋 Data updated: " + newData.size() + " items");
 | 
					        Log.d("ReprintAdapterActivity", "📋 Data updated: " + newData.size() + " items");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @NonNull
 | 
					    @NonNull
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public TransactionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
 | 
					    public TransactionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
 | 
				
			||||||
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_transaction, parent, false);
 | 
					        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_reprint, parent, false);
 | 
				
			||||||
        return new TransactionViewHolder(view);
 | 
					        return new TransactionViewHolder(view);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onBindViewHolder(@NonNull TransactionViewHolder holder, int position) {
 | 
					    public void onBindViewHolder(@NonNull TransactionViewHolder holder, int position) {
 | 
				
			||||||
        TransactionActivity.Transaction t = transactionList.get(position);
 | 
					        ReprintActivity.Transaction t = transactionList.get(position);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // ✅ STRIPE TABLE: Set alternating row colors
 | 
					        // ✅ STRIPE TABLE: Set alternating row colors
 | 
				
			||||||
        LinearLayout itemContainer = holder.itemView.findViewById(R.id.itemContainer);
 | 
					        LinearLayout itemContainer = holder.itemView.findViewById(R.id.itemContainer);
 | 
				
			||||||
@ -73,10 +76,10 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
            itemContainer.setBackgroundColor(ContextCompat.getColor(holder.itemView.getContext(), android.R.color.background_light));
 | 
					            itemContainer.setBackgroundColor(ContextCompat.getColor(holder.itemView.getContext(), android.R.color.background_light));
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionAdapter", "📋 Binding transaction " + position + ":");
 | 
					        Log.d("ReprintAdapterActivity", "📋 Binding transaction " + position + ":");
 | 
				
			||||||
        Log.d("TransactionAdapter", "   Reference: " + t.referenceId);
 | 
					        Log.d("ReprintAdapterActivity", "   Reference: " + t.referenceId);
 | 
				
			||||||
        Log.d("TransactionAdapter", "   Status: " + t.status);
 | 
					        Log.d("ReprintAdapterActivity", "   Status: " + t.status);
 | 
				
			||||||
        Log.d("TransactionAdapter", "   Amount: " + t.amount);
 | 
					        Log.d("ReprintAdapterActivity", "   Amount: " + t.amount);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Set reference ID
 | 
					        // Set reference ID
 | 
				
			||||||
        holder.referenceId.setText(t.referenceId);
 | 
					        holder.referenceId.setText(t.referenceId);
 | 
				
			||||||
@ -88,10 +91,10 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
            String formattedAmount = formatRupiah(amountValue);
 | 
					            String formattedAmount = formatRupiah(amountValue);
 | 
				
			||||||
            holder.amount.setText(formattedAmount);
 | 
					            holder.amount.setText(formattedAmount);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Log.d("TransactionAdapter", "💰 Amount processed: '" + t.amount + "' -> '" + formattedAmount + "'");
 | 
					            Log.d("ReprintAdapterActivity", "💰 Amount processed: '" + t.amount + "' -> '" + formattedAmount + "'");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
        } catch (NumberFormatException e) {
 | 
					        } catch (NumberFormatException e) {
 | 
				
			||||||
            Log.e("TransactionAdapter", "❌ Amount format error: " + t.amount, e);
 | 
					            Log.e("ReprintAdapterActivity", "❌ Amount format error: " + t.amount, e);
 | 
				
			||||||
            String fallback = t.amount.startsWith("Rp") ? t.amount : "Rp " + t.amount;
 | 
					            String fallback = t.amount.startsWith("Rp") ? t.amount : "Rp " + t.amount;
 | 
				
			||||||
            holder.amount.setText(fallback);
 | 
					            holder.amount.setText(fallback);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@ -99,7 +102,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
        // ✅ ENHANCED STATUS HANDLING dengan comprehensive checking
 | 
					        // ✅ ENHANCED STATUS HANDLING dengan comprehensive checking
 | 
				
			||||||
        String displayStatus = t.status;
 | 
					        String displayStatus = t.status;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionAdapter", "🔍 Checking status for: " + t.referenceId + " (current: " + displayStatus + ")");
 | 
					        Log.d("ReprintAdapterActivity", "🔍 Checking status for: " + t.referenceId + " (current: " + displayStatus + ")");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Jika status adalah INIT atau PENDING, lakukan comprehensive check
 | 
					        // Jika status adalah INIT atau PENDING, lakukan comprehensive check
 | 
				
			||||||
        if ("INIT".equalsIgnoreCase(t.status) || "PENDING".equalsIgnoreCase(t.status)) {
 | 
					        if ("INIT".equalsIgnoreCase(t.status) || "PENDING".equalsIgnoreCase(t.status)) {
 | 
				
			||||||
@ -108,7 +111,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                holder.status.setText("CHECKING...");
 | 
					                holder.status.setText("CHECKING...");
 | 
				
			||||||
                StyleHelper.applyStatusTextColor(holder.status, holder.itemView.getContext(), "CHECKING");
 | 
					                StyleHelper.applyStatusTextColor(holder.status, holder.itemView.getContext(), "CHECKING");
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                Log.d("TransactionAdapter", "🔄 Starting comprehensive check for: " + t.referenceId);
 | 
					                Log.d("ReprintAdapterActivity", "🔄 Starting comprehensive check for: " + t.referenceId);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // Check real status dari semua kemungkinan sources
 | 
					                // Check real status dari semua kemungkinan sources
 | 
				
			||||||
                checkMidtransStatus(t.referenceId, holder.status);
 | 
					                checkMidtransStatus(t.referenceId, holder.status);
 | 
				
			||||||
@ -116,13 +119,13 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                // No reference ID to check
 | 
					                // No reference ID to check
 | 
				
			||||||
                holder.status.setText(displayStatus.toUpperCase());
 | 
					                holder.status.setText(displayStatus.toUpperCase());
 | 
				
			||||||
                StyleHelper.applyStatusTextColor(holder.status, holder.itemView.getContext(), displayStatus);
 | 
					                StyleHelper.applyStatusTextColor(holder.status, holder.itemView.getContext(), displayStatus);
 | 
				
			||||||
                Log.w("TransactionAdapter", "⚠️ No reference ID for status check");
 | 
					                Log.w("ReprintAdapterActivity", "⚠️ No reference ID for status check");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // Use existing status yang sudah confirmed
 | 
					            // Use existing status yang sudah confirmed
 | 
				
			||||||
            holder.status.setText(displayStatus.toUpperCase());
 | 
					            holder.status.setText(displayStatus.toUpperCase());
 | 
				
			||||||
            StyleHelper.applyStatusTextColor(holder.status, holder.itemView.getContext(), displayStatus);
 | 
					            StyleHelper.applyStatusTextColor(holder.status, holder.itemView.getContext(), displayStatus);
 | 
				
			||||||
            Log.d("TransactionAdapter", "✅ Using confirmed status: " + displayStatus);
 | 
					            Log.d("ReprintAdapterActivity", "✅ Using confirmed status: " + displayStatus);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Set payment method
 | 
					        // Set payment method
 | 
				
			||||||
@ -133,7 +136,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
        String formattedDate = formatCreatedAtDate(t.createdAt);
 | 
					        String formattedDate = formatCreatedAtDate(t.createdAt);
 | 
				
			||||||
        holder.createdAt.setText(formattedDate);
 | 
					        holder.createdAt.setText(formattedDate);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionAdapter", "📅 Created at: " + t.createdAt + " -> " + formattedDate);
 | 
					        Log.d("ReprintAdapterActivity", "📅 Created at: " + t.createdAt + " -> " + formattedDate);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Set click listeners
 | 
					        // Set click listeners
 | 
				
			||||||
        holder.itemView.setOnClickListener(v -> {
 | 
					        holder.itemView.setOnClickListener(v -> {
 | 
				
			||||||
@ -148,7 +151,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
            }
 | 
					            }
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionAdapter", "✅ Transaction binding complete for: " + t.referenceId);
 | 
					        Log.d("ReprintAdapterActivity", "✅ Transaction binding complete for: " + t.referenceId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private String cleanAmountString(String amount) {
 | 
					    private String cleanAmountString(String amount) {
 | 
				
			||||||
@ -156,7 +159,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
            return "0";
 | 
					            return "0";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionAdapter", "Cleaning amount: '" + amount + "'");
 | 
					        Log.d("ReprintAdapterActivity", "Cleaning amount: '" + amount + "'");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Remove currency symbols and spaces
 | 
					        // Remove currency symbols and spaces
 | 
				
			||||||
        String cleaned = amount
 | 
					        String cleaned = amount
 | 
				
			||||||
@ -199,7 +202,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
        // Remove any commas
 | 
					        // Remove any commas
 | 
				
			||||||
        cleaned = cleaned.replace(",", "");
 | 
					        cleaned = cleaned.replace(",", "");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionAdapter", "Cleaned result: '" + cleaned + "'");
 | 
					        Log.d("ReprintAdapterActivity", "Cleaned result: '" + cleaned + "'");
 | 
				
			||||||
        return cleaned;
 | 
					        return cleaned;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -216,7 +219,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
    private void checkMidtransStatus(String referenceId, TextView statusTextView) {
 | 
					    private void checkMidtransStatus(String referenceId, TextView statusTextView) {
 | 
				
			||||||
        new Thread(() -> {
 | 
					        new Thread(() -> {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                Log.d("TransactionAdapter", "🔍 Comprehensive status check for reference: " + referenceId);
 | 
					                Log.d("ReprintAdapterActivity", "🔍 Comprehensive status check for reference: " + referenceId);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // STEP 1: Query webhook logs untuk semua order_id yang terkait
 | 
					                // STEP 1: Query webhook logs untuk semua order_id yang terkait
 | 
				
			||||||
                String queryUrl = "https://be-edc.msvc.app/api-logs?limit=200&sortOrder=DESC&sortColumn=created_at";
 | 
					                String queryUrl = "https://be-edc.msvc.app/api-logs?limit=200&sortOrder=DESC&sortColumn=created_at";
 | 
				
			||||||
@ -244,7 +247,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                    String foundAcquirer = null;
 | 
					                    String foundAcquirer = null;
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    if (results != null && results.length() > 0) {
 | 
					                    if (results != null && results.length() > 0) {
 | 
				
			||||||
                        Log.d("TransactionAdapter", "📊 Processing " + results.length() + " log entries");
 | 
					                        Log.d("ReprintAdapterActivity", "📊 Processing " + results.length() + " log entries");
 | 
				
			||||||
                        
 | 
					                        
 | 
				
			||||||
                        // STEP 2: Comprehensive search dengan multiple matching strategies
 | 
					                        // STEP 2: Comprehensive search dengan multiple matching strategies
 | 
				
			||||||
                        for (int i = 0; i < results.length(); i++) {
 | 
					                        for (int i = 0; i < results.length(); i++) {
 | 
				
			||||||
@ -270,7 +273,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                                        String appReferenceId = customData.optString("app_reference_id", "");
 | 
					                                        String appReferenceId = customData.optString("app_reference_id", "");
 | 
				
			||||||
                                        if (referenceId.equals(originalReference) || referenceId.equals(appReferenceId)) {
 | 
					                                        if (referenceId.equals(originalReference) || referenceId.equals(appReferenceId)) {
 | 
				
			||||||
                                            isRefreshMatch = true;
 | 
					                                            isRefreshMatch = true;
 | 
				
			||||||
                                            Log.d("TransactionAdapter", "🔄 Found refresh match: " + logOrderId);
 | 
					                                            Log.d("ReprintAdapterActivity", "🔄 Found refresh match: " + logOrderId);
 | 
				
			||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
                                    } catch (JSONException e) {
 | 
					                                    } catch (JSONException e) {
 | 
				
			||||||
                                        // Ignore custom field parsing errors
 | 
					                                        // Ignore custom field parsing errors
 | 
				
			||||||
@ -288,7 +291,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                                            if (itemName.contains("(Ref: " + referenceId + ")") || 
 | 
					                                            if (itemName.contains("(Ref: " + referenceId + ")") || 
 | 
				
			||||||
                                                itemName.contains("- " + referenceId)) {
 | 
					                                                itemName.contains("- " + referenceId)) {
 | 
				
			||||||
                                                isItemMatch = true;
 | 
					                                                isItemMatch = true;
 | 
				
			||||||
                                                Log.d("TransactionAdapter", "📦 Found item match: " + logOrderId);
 | 
					                                                Log.d("ReprintAdapterActivity", "📦 Found item match: " + logOrderId);
 | 
				
			||||||
                                                break;
 | 
					                                                break;
 | 
				
			||||||
                                            }
 | 
					                                            }
 | 
				
			||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
@ -299,11 +302,11 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                                boolean isRelatedTransaction = isDirectMatch || isRefreshMatch || isItemMatch;
 | 
					                                boolean isRelatedTransaction = isDirectMatch || isRefreshMatch || isItemMatch;
 | 
				
			||||||
                                
 | 
					                                
 | 
				
			||||||
                                if (isRelatedTransaction) {
 | 
					                                if (isRelatedTransaction) {
 | 
				
			||||||
                                    Log.d("TransactionAdapter", "🎯 MATCH FOUND!");
 | 
					                                    Log.d("ReprintAdapterActivity", "🎯 MATCH FOUND!");
 | 
				
			||||||
                                    Log.d("TransactionAdapter", "   Order ID: " + logOrderId);
 | 
					                                    Log.d("ReprintAdapterActivity", "   Order ID: " + logOrderId);
 | 
				
			||||||
                                    Log.d("TransactionAdapter", "   Status: " + logTransactionStatus);
 | 
					                                    Log.d("ReprintAdapterActivity", "   Status: " + logTransactionStatus);
 | 
				
			||||||
                                    Log.d("TransactionAdapter", "   Acquirer: " + logAcquirer);
 | 
					                                    Log.d("ReprintAdapterActivity", "   Acquirer: " + logAcquirer);
 | 
				
			||||||
                                    Log.d("TransactionAdapter", "   Match Type: " + 
 | 
					                                    Log.d("ReprintAdapterActivity", "   Match Type: " + 
 | 
				
			||||||
                                        (isDirectMatch ? "DIRECT " : "") + 
 | 
					                                        (isDirectMatch ? "DIRECT " : "") + 
 | 
				
			||||||
                                        (isRefreshMatch ? "REFRESH " : "") + 
 | 
					                                        (isRefreshMatch ? "REFRESH " : "") + 
 | 
				
			||||||
                                        (isItemMatch ? "ITEM" : ""));
 | 
					                                        (isItemMatch ? "ITEM" : ""));
 | 
				
			||||||
@ -315,29 +318,29 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                                        finalStatus = "PAID";
 | 
					                                        finalStatus = "PAID";
 | 
				
			||||||
                                        foundOrderId = logOrderId;
 | 
					                                        foundOrderId = logOrderId;
 | 
				
			||||||
                                        foundAcquirer = logAcquirer;
 | 
					                                        foundAcquirer = logAcquirer;
 | 
				
			||||||
                                        Log.d("TransactionAdapter", "✅ PAYMENT CONFIRMED: " + logOrderId + " -> " + logTransactionStatus);
 | 
					                                        Log.d("ReprintAdapterActivity", "✅ PAYMENT CONFIRMED: " + logOrderId + " -> " + logTransactionStatus);
 | 
				
			||||||
                                        break; // Found paid status, stop searching
 | 
					                                        break; // Found paid status, stop searching
 | 
				
			||||||
                                    } else if (logTransactionStatus.equals("pending") && finalStatus.equals("INIT")) {
 | 
					                                    } else if (logTransactionStatus.equals("pending") && finalStatus.equals("INIT")) {
 | 
				
			||||||
                                        finalStatus = "PENDING";
 | 
					                                        finalStatus = "PENDING";
 | 
				
			||||||
                                        foundOrderId = logOrderId;
 | 
					                                        foundOrderId = logOrderId;
 | 
				
			||||||
                                        foundAcquirer = logAcquirer;
 | 
					                                        foundAcquirer = logAcquirer;
 | 
				
			||||||
                                        Log.d("TransactionAdapter", "⏳ PENDING found: " + logOrderId);
 | 
					                                        Log.d("ReprintAdapterActivity", "⏳ PENDING found: " + logOrderId);
 | 
				
			||||||
                                    } else if (logTransactionStatus.equals("expire") || logTransactionStatus.equals("cancel")) {
 | 
					                                    } else if (logTransactionStatus.equals("expire") || logTransactionStatus.equals("cancel")) {
 | 
				
			||||||
                                        if (finalStatus.equals("INIT")) { // Only update if no better status found
 | 
					                                        if (finalStatus.equals("INIT")) { // Only update if no better status found
 | 
				
			||||||
                                            finalStatus = "FAILED";
 | 
					                                            finalStatus = "FAILED";
 | 
				
			||||||
                                            foundOrderId = logOrderId;
 | 
					                                            foundOrderId = logOrderId;
 | 
				
			||||||
                                            foundAcquirer = logAcquirer;
 | 
					                                            foundAcquirer = logAcquirer;
 | 
				
			||||||
                                            Log.d("TransactionAdapter", "❌ FAILED status: " + logOrderId + " -> " + logTransactionStatus);
 | 
					                                            Log.d("ReprintAdapterActivity", "❌ FAILED status: " + logOrderId + " -> " + logTransactionStatus);
 | 
				
			||||||
                                        }
 | 
					                                        }
 | 
				
			||||||
                                    }
 | 
					                                    }
 | 
				
			||||||
                                }
 | 
					                                }
 | 
				
			||||||
                            }
 | 
					                            }
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                        
 | 
					                        
 | 
				
			||||||
                        Log.d("TransactionAdapter", "🔍 FINAL RESULT for " + referenceId + ":");
 | 
					                        Log.d("ReprintAdapterActivity", "🔍 FINAL RESULT for " + referenceId + ":");
 | 
				
			||||||
                        Log.d("TransactionAdapter", "   Status: " + finalStatus);
 | 
					                        Log.d("ReprintAdapterActivity", "   Status: " + finalStatus);
 | 
				
			||||||
                        Log.d("TransactionAdapter", "   Order ID: " + (foundOrderId != null ? foundOrderId : "N/A"));
 | 
					                        Log.d("ReprintAdapterActivity", "   Order ID: " + (foundOrderId != null ? foundOrderId : "N/A"));
 | 
				
			||||||
                        Log.d("TransactionAdapter", "   Acquirer: " + (foundAcquirer != null ? foundAcquirer : "N/A"));
 | 
					                        Log.d("ReprintAdapterActivity", "   Acquirer: " + (foundAcquirer != null ? foundAcquirer : "N/A"));
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    // STEP 3: Update UI di main thread
 | 
					                    // STEP 3: Update UI di main thread
 | 
				
			||||||
@ -348,10 +351,10 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                        statusTextView.setText(displayStatus);
 | 
					                        statusTextView.setText(displayStatus);
 | 
				
			||||||
                        StyleHelper.applyStatusTextColor(statusTextView, statusTextView.getContext(), displayStatus);
 | 
					                        StyleHelper.applyStatusTextColor(statusTextView, statusTextView.getContext(), displayStatus);
 | 
				
			||||||
                        
 | 
					                        
 | 
				
			||||||
                        Log.d("TransactionAdapter", "🎨 UI UPDATED:");
 | 
					                        Log.d("ReprintAdapterActivity", "🎨 UI UPDATED:");
 | 
				
			||||||
                        Log.d("TransactionAdapter", "   Reference: " + referenceId);
 | 
					                        Log.d("ReprintAdapterActivity", "   Reference: " + referenceId);
 | 
				
			||||||
                        Log.d("TransactionAdapter", "   Display Status: " + displayStatus);
 | 
					                        Log.d("ReprintAdapterActivity", "   Display Status: " + displayStatus);
 | 
				
			||||||
                        Log.d("TransactionAdapter", "   Detected Acquirer: " + (detectedAcquirer != null ? detectedAcquirer : "Unknown"));
 | 
					                        Log.d("ReprintAdapterActivity", "   Detected Acquirer: " + (detectedAcquirer != null ? detectedAcquirer : "Unknown"));
 | 
				
			||||||
                    });
 | 
					                    });
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                    // ✅ BONUS: Update backend jika status berubah ke PAID
 | 
					                    // ✅ BONUS: Update backend jika status berubah ke PAID
 | 
				
			||||||
@ -360,7 +363,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    Log.w("TransactionAdapter", "⚠️ API call failed with code: " + conn.getResponseCode());
 | 
					                    Log.w("ReprintAdapterActivity", "⚠️ API call failed with code: " + conn.getResponseCode());
 | 
				
			||||||
                    statusTextView.post(() -> {
 | 
					                    statusTextView.post(() -> {
 | 
				
			||||||
                        statusTextView.setText("ERROR");
 | 
					                        statusTextView.setText("ERROR");
 | 
				
			||||||
                        StyleHelper.applyStatusTextColor(statusTextView, statusTextView.getContext(), "ERROR");
 | 
					                        StyleHelper.applyStatusTextColor(statusTextView, statusTextView.getContext(), "ERROR");
 | 
				
			||||||
@ -368,7 +371,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
            } catch (IOException | JSONException e) {
 | 
					            } catch (IOException | JSONException e) {
 | 
				
			||||||
                Log.e("TransactionAdapter", "❌ Comprehensive status check error: " + e.getMessage(), e);
 | 
					                Log.e("ReprintAdapterActivity", "❌ Comprehensive status check error: " + e.getMessage(), e);
 | 
				
			||||||
                statusTextView.post(() -> {
 | 
					                statusTextView.post(() -> {
 | 
				
			||||||
                    statusTextView.setText("INIT");
 | 
					                    statusTextView.setText("INIT");
 | 
				
			||||||
                    StyleHelper.applyStatusTextColor(statusTextView, statusTextView.getContext(), "INIT");
 | 
					                    StyleHelper.applyStatusTextColor(statusTextView, statusTextView.getContext(), "INIT");
 | 
				
			||||||
@ -383,7 +386,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
    private void updateBackendTransactionStatus(String referenceId, String status, String orderId, String acquirer) {
 | 
					    private void updateBackendTransactionStatus(String referenceId, String status, String orderId, String acquirer) {
 | 
				
			||||||
        new Thread(() -> {
 | 
					        new Thread(() -> {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                Log.d("TransactionAdapter", "🔄 Updating backend status for reference: " + referenceId);
 | 
					                Log.d("ReprintAdapterActivity", "🔄 Updating backend status for reference: " + referenceId);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                JSONObject updatePayload = new JSONObject();
 | 
					                JSONObject updatePayload = new JSONObject();
 | 
				
			||||||
                updatePayload.put("status", status);
 | 
					                updatePayload.put("status", status);
 | 
				
			||||||
@ -414,16 +417,16 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                int responseCode = conn.getResponseCode();
 | 
					                int responseCode = conn.getResponseCode();
 | 
				
			||||||
                Log.d("TransactionAdapter", "📥 Backend update response: " + responseCode);
 | 
					                Log.d("ReprintAdapterActivity", "📥 Backend update response: " + responseCode);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                if (responseCode == 200 || responseCode == 201) {
 | 
					                if (responseCode == 200 || responseCode == 201) {
 | 
				
			||||||
                    Log.d("TransactionAdapter", "✅ Backend status updated successfully");
 | 
					                    Log.d("ReprintAdapterActivity", "✅ Backend status updated successfully");
 | 
				
			||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    Log.e("TransactionAdapter", "❌ Backend update failed: " + responseCode);
 | 
					                    Log.e("ReprintAdapterActivity", "❌ Backend update failed: " + responseCode);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
            } catch (Exception e) {
 | 
					            } catch (Exception e) {
 | 
				
			||||||
                Log.e("TransactionAdapter", "❌ Backend update error: " + e.getMessage(), e);
 | 
					                Log.e("ReprintAdapterActivity", "❌ Backend update error: " + e.getMessage(), e);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }).start();
 | 
					        }).start();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -436,7 +439,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
            return "N/A";
 | 
					            return "N/A";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("TransactionAdapter", "📅 Input date: '" + rawDate + "'");
 | 
					        Log.d("ReprintAdapterActivity", "📅 Input date: '" + rawDate + "'");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            // Handle different possible input formats from API
 | 
					            // Handle different possible input formats from API
 | 
				
			||||||
@ -460,7 +463,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
					                inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Log.d("TransactionAdapter", "📅 Cleaned date: '" + cleanedDate + "'");
 | 
					            Log.d("ReprintAdapterActivity", "📅 Cleaned date: '" + cleanedDate + "'");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Output format: d/M/yyyy H:mm:ss
 | 
					            // Output format: d/M/yyyy H:mm:ss
 | 
				
			||||||
            SimpleDateFormat outputFormat = new SimpleDateFormat("d/M/yyyy H:mm:ss", Locale.getDefault());
 | 
					            SimpleDateFormat outputFormat = new SimpleDateFormat("d/M/yyyy H:mm:ss", Locale.getDefault());
 | 
				
			||||||
@ -468,11 +471,11 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
            Date date = inputFormat.parse(cleanedDate);
 | 
					            Date date = inputFormat.parse(cleanedDate);
 | 
				
			||||||
            if (date != null) {
 | 
					            if (date != null) {
 | 
				
			||||||
                String formatted = outputFormat.format(date);
 | 
					                String formatted = outputFormat.format(date);
 | 
				
			||||||
                Log.d("TransactionAdapter", "📅 Date formatted: " + rawDate + " -> " + formatted);
 | 
					                Log.d("ReprintAdapterActivity", "📅 Date formatted: " + rawDate + " -> " + formatted);
 | 
				
			||||||
                return formatted;
 | 
					                return formatted;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } catch (Exception e) {
 | 
				
			||||||
            Log.e("TransactionAdapter", "❌ Date formatting error for: " + rawDate, e);
 | 
					            Log.e("ReprintAdapterActivity", "❌ Date formatting error for: " + rawDate, e);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Fallback: Manual parsing
 | 
					        // Fallback: Manual parsing
 | 
				
			||||||
@ -485,7 +488,7 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                workingDate = workingDate.substring(0, workingDate.indexOf("."));
 | 
					                workingDate = workingDate.substring(0, workingDate.indexOf("."));
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Log.d("TransactionAdapter", "📅 Manual parsing attempt: '" + workingDate + "'");
 | 
					            Log.d("ReprintAdapterActivity", "📅 Manual parsing attempt: '" + workingDate + "'");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Split into date and time parts
 | 
					            // Split into date and time parts
 | 
				
			||||||
            String[] parts = workingDate.split(" ");
 | 
					            String[] parts = workingDate.split(" ");
 | 
				
			||||||
@ -511,16 +514,16 @@ public class TransactionAdapter extends RecyclerView.Adapter<TransactionAdapter.
 | 
				
			|||||||
                        String second = timeComponents[2];
 | 
					                        String second = timeComponents[2];
 | 
				
			||||||
                        
 | 
					                        
 | 
				
			||||||
                        String result = dayInt + "/" + monthInt + "/" + year + " " + hour + ":" + minute + ":" + second;
 | 
					                        String result = dayInt + "/" + monthInt + "/" + year + " " + hour + ":" + minute + ":" + second;
 | 
				
			||||||
                        Log.d("TransactionAdapter", "📅 Manual format result: " + result);
 | 
					                        Log.d("ReprintAdapterActivity", "📅 Manual format result: " + result);
 | 
				
			||||||
                        return result;
 | 
					                        return result;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } catch (Exception e) {
 | 
					        } catch (Exception e) {
 | 
				
			||||||
            Log.w("TransactionAdapter", "❌ Manual date formatting failed: " + e.getMessage());
 | 
					            Log.w("ReprintAdapterActivity", "❌ Manual date formatting failed: " + e.getMessage());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.w("TransactionAdapter", "📅 Using fallback - returning original date: " + rawDate);
 | 
					        Log.w("ReprintAdapterActivity", "📅 Using fallback - returning original date: " + rawDate);
 | 
				
			||||||
        return rawDate;
 | 
					        return rawDate;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -22,21 +22,24 @@ import com.example.bdkipoc.transaction.managers.CardScannerManager;
 | 
				
			|||||||
import com.example.bdkipoc.transaction.managers.EMVManager;
 | 
					import com.example.bdkipoc.transaction.managers.EMVManager;
 | 
				
			||||||
import com.example.bdkipoc.transaction.managers.ModalManager;
 | 
					import com.example.bdkipoc.transaction.managers.ModalManager;
 | 
				
			||||||
import com.example.bdkipoc.transaction.managers.PinPadManager;
 | 
					import com.example.bdkipoc.transaction.managers.PinPadManager;
 | 
				
			||||||
 | 
					import com.example.bdkipoc.transaction.managers.MidtransCardPaymentManager;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.text.NumberFormat;
 | 
					import java.text.NumberFormat;
 | 
				
			||||||
import java.util.Locale;
 | 
					import java.util.Locale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.example.bdkipoc.kredit.CreditCardActivity;
 | 
					import com.example.bdkipoc.transaction.ResultTransactionActivity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.json.JSONObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * CreateTransactionActivity - Updated UI to match screenshot design
 | 
					 * CreateTransactionActivity - Updated with Midtrans Credit Card Integration
 | 
				
			||||||
 * Handles amount input and card scanning in one screen
 | 
					 * Handles amount input, card scanning, and Midtrans payment processing
 | 
				
			||||||
 * Located in transaction package
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class CreateTransactionActivity extends AppCompatActivity implements 
 | 
					public class CreateTransactionActivity extends AppCompatActivity implements 
 | 
				
			||||||
    EMVManager.EMVManagerCallback,
 | 
					    EMVManager.EMVManagerCallback,
 | 
				
			||||||
    CardScannerManager.CardScannerCallback,
 | 
					    CardScannerManager.CardScannerCallback,
 | 
				
			||||||
    PinPadManager.PinPadManagerCallback {
 | 
					    PinPadManager.PinPadManagerCallback,
 | 
				
			||||||
 | 
					    MidtransCardPaymentManager.MidtransCardPaymentCallback {
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    private static final String TAG = "CreateTransaction";
 | 
					    private static final String TAG = "CreateTransaction";
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@ -55,17 +58,27 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
    // State Management
 | 
					    // State Management
 | 
				
			||||||
    private String transactionAmount = "0";
 | 
					    private String transactionAmount = "0";
 | 
				
			||||||
    private boolean isEMVMode = true;
 | 
					    private boolean isEMVMode = true;
 | 
				
			||||||
 | 
					    private boolean useDirectMidtransPayment = true; // Toggle for Midtrans integration
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // Manager Classes
 | 
					    // Manager Classes
 | 
				
			||||||
    private EMVManager emvManager;
 | 
					    private EMVManager emvManager;
 | 
				
			||||||
    private CardScannerManager cardScannerManager;
 | 
					    private CardScannerManager cardScannerManager;
 | 
				
			||||||
    private PinPadManager pinPadManager;
 | 
					    private PinPadManager pinPadManager;
 | 
				
			||||||
    private ModalManager modalManager;
 | 
					    private ModalManager modalManager;
 | 
				
			||||||
 | 
					    private MidtransCardPaymentManager midtransPaymentManager; // ✅ NEW: Midtrans integration
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // EMV Dialog
 | 
					    // EMV Dialog
 | 
				
			||||||
    private AlertDialog mAppSelectDialog;
 | 
					    private AlertDialog mAppSelectDialog;
 | 
				
			||||||
    private int mSelectIndex;
 | 
					    private int mSelectIndex;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ NEW: EMV Data Storage for Midtrans
 | 
				
			||||||
 | 
					    private String emvCardNumber;
 | 
				
			||||||
 | 
					    private String emvExpiryDate;
 | 
				
			||||||
 | 
					    private String emvCardholderName;
 | 
				
			||||||
 | 
					    private String emvAidIdentifier;
 | 
				
			||||||
 | 
					    private String emvTlvData;
 | 
				
			||||||
 | 
					    private String referenceId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
					    protected void onCreate(Bundle savedInstanceState) {
 | 
				
			||||||
        super.onCreate(savedInstanceState);
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
@ -76,6 +89,10 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        setupListeners();
 | 
					        setupListeners();
 | 
				
			||||||
        updateAmountDisplay();
 | 
					        updateAmountDisplay();
 | 
				
			||||||
        updateModeDisplay();
 | 
					        updateModeDisplay();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ NEW: Generate reference ID for transaction tracking
 | 
				
			||||||
 | 
					        referenceId = generateReferenceId();
 | 
				
			||||||
 | 
					        Log.d(TAG, "Generated reference ID: " + referenceId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void initViews() {
 | 
					    private void initViews() {
 | 
				
			||||||
@ -114,10 +131,13 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        cardScannerManager = new CardScannerManager(this);
 | 
					        cardScannerManager = new CardScannerManager(this);
 | 
				
			||||||
        pinPadManager = new PinPadManager(this);
 | 
					        pinPadManager = new PinPadManager(this);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ NEW: Initialize Midtrans payment manager
 | 
				
			||||||
 | 
					        midtransPaymentManager = new MidtransCardPaymentManager(this, this);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        // Initialize EMV data
 | 
					        // Initialize EMV data
 | 
				
			||||||
        emvManager.initEMVData();
 | 
					        emvManager.initEMVData();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d(TAG, "All managers initialized");
 | 
					        Log.d(TAG, "All managers initialized including Midtrans");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void setupListeners() {
 | 
					    private void setupListeners() {
 | 
				
			||||||
@ -245,7 +265,7 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        Log.d(TAG, "Simple card detected: " + cardType);
 | 
					        Log.d(TAG, "Simple card detected: " + cardType);
 | 
				
			||||||
        modalManager.showProcessingModal("Kartu " + cardType + " Ditemukan - Memproses...");
 | 
					        modalManager.showProcessingModal("Kartu " + cardType + " Ditemukan - Memproses...");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Navigate to results after short delay
 | 
					        // For simple mode, navigate to results without Midtrans
 | 
				
			||||||
        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
					        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
				
			||||||
            navigateToResults(cardType, cardData, null);
 | 
					            navigateToResults(cardType, cardData, null);
 | 
				
			||||||
        }, 1500);
 | 
					        }, 1500);
 | 
				
			||||||
@ -290,6 +310,10 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onConfirmCardNo(String cardNo) {
 | 
					    public void onConfirmCardNo(String cardNo) {
 | 
				
			||||||
 | 
					        // ✅ NEW: Store EMV card data for Midtrans
 | 
				
			||||||
 | 
					        emvCardNumber = cardNo;
 | 
				
			||||||
 | 
					        Log.d(TAG, "EMV Card Number extracted: " + maskCardNumber(cardNo));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        modalManager.showProcessingModal("Mengkonfirmasi Nomor Kartu...");
 | 
					        modalManager.showProcessingModal("Mengkonfirmasi Nomor Kartu...");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        // Auto-confirm after short delay
 | 
					        // Auto-confirm after short delay
 | 
				
			||||||
@ -340,9 +364,15 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        Log.d(TAG, "EMV Transaction successful");
 | 
					        Log.d(TAG, "EMV Transaction successful");
 | 
				
			||||||
        modalManager.hideModal();
 | 
					        modalManager.hideModal();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ NEW: Process Midtrans payment after successful EMV
 | 
				
			||||||
 | 
					        if (useDirectMidtransPayment && emvCardNumber != null) {
 | 
				
			||||||
 | 
					            processMidtransPayment();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Traditional flow - navigate to results
 | 
				
			||||||
            String cardType = emvManager.getCardType() == com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
					            String cardType = emvManager.getCardType() == com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
				
			||||||
            navigateToResults(cardType, null, emvManager.getCardNo());
 | 
					            navigateToResults(cardType, null, emvManager.getCardNo());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onTransactionFailed(int code, String desc) {
 | 
					    public void onTransactionFailed(int code, String desc) {
 | 
				
			||||||
@ -364,11 +394,6 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
    // ====== PIN PAD CALLBACK METHODS ======
 | 
					    // ====== PIN PAD CALLBACK METHODS ======
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onPinInputLength(int length) {
 | 
					    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);
 | 
					        Log.d(TAG, "PIN input length: " + length);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -398,6 +423,131 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        emvManager.importPinInputStatus(3); // Error
 | 
					        emvManager.importPinInputStatus(3); // Error
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ====== ✅ NEW: MIDTRANS PAYMENT CALLBACK METHODS ======
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onTokenizeSuccess(String cardToken) {
 | 
				
			||||||
 | 
					        Log.d(TAG, "✅ Midtrans tokenization successful: " + cardToken);
 | 
				
			||||||
 | 
					        // Tokenization successful, charge process will continue automatically
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onTokenizeError(String errorMessage) {
 | 
				
			||||||
 | 
					        Log.e(TAG, "❌ Midtrans tokenization failed: " + errorMessage);
 | 
				
			||||||
 | 
					        modalManager.hideModal();
 | 
				
			||||||
 | 
					        showToast("Payment tokenization failed: " + errorMessage);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Fallback to traditional results screen
 | 
				
			||||||
 | 
					        String cardType = emvManager.getCardType() == com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
				
			||||||
 | 
					        navigateToResults(cardType, null, emvManager.getCardNo());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onChargeSuccess(JSONObject chargeResponse) {
 | 
				
			||||||
 | 
					        Log.d(TAG, "✅ Midtrans charge successful!");
 | 
				
			||||||
 | 
					        modalManager.hideModal();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            String transactionId = chargeResponse.getString("transaction_id");
 | 
				
			||||||
 | 
					            String transactionStatus = chargeResponse.getString("transaction_status");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Log.d(TAG, "Transaction ID: " + transactionId);
 | 
				
			||||||
 | 
					            Log.d(TAG, "Transaction Status: " + transactionStatus);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Navigate to success results with Midtrans data
 | 
				
			||||||
 | 
					            navigateToMidtransResults(chargeResponse);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error parsing Midtrans response: " + e.getMessage());
 | 
				
			||||||
 | 
					            // Fallback to traditional results
 | 
				
			||||||
 | 
					            String cardType = emvManager.getCardType() == com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
				
			||||||
 | 
					            navigateToResults(cardType, null, emvManager.getCardNo());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onChargeError(String errorMessage) {
 | 
				
			||||||
 | 
					        Log.e(TAG, "❌ Midtrans charge failed: " + errorMessage);
 | 
				
			||||||
 | 
					        modalManager.hideModal();
 | 
				
			||||||
 | 
					        showToast("Payment processing failed: " + errorMessage);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Fallback to traditional results screen
 | 
				
			||||||
 | 
					        String cardType = emvManager.getCardType() == com.sunmi.pay.hardware.aidlv2.AidlConstantsV2.CardType.NFC.getValue() ? "NFC" : "IC";
 | 
				
			||||||
 | 
					        navigateToResults(cardType, null, emvManager.getCardNo());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onPaymentProgress(String message) {
 | 
				
			||||||
 | 
					        Log.d(TAG, "Midtrans payment progress: " + message);
 | 
				
			||||||
 | 
					        modalManager.showProcessingModal(message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ====== ✅ NEW: MIDTRANS PAYMENT PROCESSING ======
 | 
				
			||||||
 | 
					    private void processMidtransPayment() {
 | 
				
			||||||
 | 
					        Log.d(TAG, "=== STARTING MIDTRANS PAYMENT PROCESS ===");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Extract additional EMV data if available
 | 
				
			||||||
 | 
					            extractAdditionalEMVData();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Create card data object from EMV information
 | 
				
			||||||
 | 
					            MidtransCardPaymentManager.CardData cardData = 
 | 
				
			||||||
 | 
					                MidtransCardPaymentManager.CardData.fromEMVData(
 | 
				
			||||||
 | 
					                    emvCardNumber, 
 | 
				
			||||||
 | 
					                    emvExpiryDate, 
 | 
				
			||||||
 | 
					                    emvCardholderName, 
 | 
				
			||||||
 | 
					                    emvAidIdentifier
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Log.d(TAG, "EMV Card Data prepared:");
 | 
				
			||||||
 | 
					            Log.d(TAG, "  - PAN: " + maskCardNumber(cardData.getPan()));
 | 
				
			||||||
 | 
					            Log.d(TAG, "  - Expiry: " + cardData.getExpiryMonth() + "/" + cardData.getExpiryYear());
 | 
				
			||||||
 | 
					            Log.d(TAG, "  - Cardholder: " + cardData.getCardholderName());
 | 
				
			||||||
 | 
					            Log.d(TAG, "  - AID: " + cardData.getAidIdentifier());
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (!cardData.isValid()) {
 | 
				
			||||||
 | 
					                Log.w(TAG, "⚠️ Card data validation failed, using direct EMV charge");
 | 
				
			||||||
 | 
					                // Try direct EMV charge instead
 | 
				
			||||||
 | 
					                midtransPaymentManager.processEMVDirectCharge(
 | 
				
			||||||
 | 
					                    cardData, 
 | 
				
			||||||
 | 
					                    Long.parseLong(transactionAmount), 
 | 
				
			||||||
 | 
					                    referenceId, 
 | 
				
			||||||
 | 
					                    emvTlvData
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Process normal card payment (with tokenization)
 | 
				
			||||||
 | 
					                midtransPaymentManager.processCardPayment(
 | 
				
			||||||
 | 
					                    cardData, 
 | 
				
			||||||
 | 
					                    Long.parseLong(transactionAmount), 
 | 
				
			||||||
 | 
					                    referenceId
 | 
				
			||||||
 | 
					                );
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error preparing Midtrans payment: " + e.getMessage(), e);
 | 
				
			||||||
 | 
					            onChargeError("Failed to prepare payment data: " + e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void extractAdditionalEMVData() {
 | 
				
			||||||
 | 
					        // This method would extract additional EMV data from the completed transaction
 | 
				
			||||||
 | 
					        // For now, we'll use placeholder data - in real implementation, 
 | 
				
			||||||
 | 
					        // you would extract this from EMV TLV data
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Example: Extract cardholder name from tag 5F20
 | 
				
			||||||
 | 
					        emvCardholderName = "EMV CARDHOLDER"; // Placeholder
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Example: Extract expiry date from tag 5F24  
 | 
				
			||||||
 | 
					        emvExpiryDate = "251220"; // Placeholder - format YYMMDD
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Example: Extract AID from tag 9F06
 | 
				
			||||||
 | 
					        emvAidIdentifier = "A0000000031010"; // Placeholder - Visa AID
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Example: Collect relevant TLV data for EMV processing
 | 
				
			||||||
 | 
					        emvTlvData = "9F2608=1234567890ABCDEF;9F2701=80;9F3602=0001"; // Placeholder
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "Additional EMV data extracted");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // ====== HELPER METHODS ======
 | 
					    // ====== HELPER METHODS ======
 | 
				
			||||||
    private void showAppSelectDialog(String[] candidateNames) {
 | 
					    private void showAppSelectDialog(String[] candidateNames) {
 | 
				
			||||||
        mAppSelectDialog = new AlertDialog.Builder(this)
 | 
					        mAppSelectDialog = new AlertDialog.Builder(this)
 | 
				
			||||||
@ -413,10 +563,11 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
    private void navigateToResults(String cardType, Bundle cardData, String cardNo) {
 | 
					    private void navigateToResults(String cardType, Bundle cardData, String cardNo) {
 | 
				
			||||||
        modalManager.hideModal();
 | 
					        modalManager.hideModal();
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Intent intent = new Intent(this, CreditCardActivity.class);
 | 
					        Intent intent = new Intent(this, ResultTransactionActivity.class);
 | 
				
			||||||
        intent.putExtra("TRANSACTION_AMOUNT", transactionAmount);
 | 
					        intent.putExtra("TRANSACTION_AMOUNT", transactionAmount);
 | 
				
			||||||
        intent.putExtra("CARD_TYPE", cardType);
 | 
					        intent.putExtra("CARD_TYPE", cardType);
 | 
				
			||||||
        intent.putExtra("EMV_MODE", isEMVMode);
 | 
					        intent.putExtra("EMV_MODE", isEMVMode);
 | 
				
			||||||
 | 
					        intent.putExtra("REFERENCE_ID", referenceId);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if (cardData != null) {
 | 
					        if (cardData != null) {
 | 
				
			||||||
            intent.putExtra("CARD_DATA", cardData);
 | 
					            intent.putExtra("CARD_DATA", cardData);
 | 
				
			||||||
@ -429,6 +580,23 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        finish();
 | 
					        finish();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ NEW: Navigate to results with Midtrans payment data
 | 
				
			||||||
 | 
					    private void navigateToMidtransResults(JSONObject midtransResponse) {
 | 
				
			||||||
 | 
					        modalManager.hideModal();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Intent intent = new Intent(this, ResultTransactionActivity.class);
 | 
				
			||||||
 | 
					        intent.putExtra("TRANSACTION_AMOUNT", transactionAmount);
 | 
				
			||||||
 | 
					        intent.putExtra("CARD_TYPE", "EMV_MIDTRANS");
 | 
				
			||||||
 | 
					        intent.putExtra("EMV_MODE", true);
 | 
				
			||||||
 | 
					        intent.putExtra("REFERENCE_ID", referenceId);
 | 
				
			||||||
 | 
					        intent.putExtra("CARD_NO", emvCardNumber);
 | 
				
			||||||
 | 
					        intent.putExtra("MIDTRANS_RESPONSE", midtransResponse.toString());
 | 
				
			||||||
 | 
					        intent.putExtra("PAYMENT_SUCCESS", true);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        startActivity(intent);
 | 
				
			||||||
 | 
					        finish();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
    private void restartScanningAfterDelay() {
 | 
					    private void restartScanningAfterDelay() {
 | 
				
			||||||
        Log.d(TAG, "Restarting scanning after delay...");
 | 
					        Log.d(TAG, "Restarting scanning after delay...");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@ -472,6 +640,24 @@ public class CreateTransactionActivity extends AppCompatActivity implements
 | 
				
			|||||||
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
 | 
					        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ NEW: Generate reference ID for transaction tracking
 | 
				
			||||||
 | 
					    private String generateReferenceId() {
 | 
				
			||||||
 | 
					        return "ref-" + System.currentTimeMillis() + "-" + (int)(Math.random() * 10000);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String maskCardNumber(String cardNumber) {
 | 
				
			||||||
 | 
					        if (cardNumber == null || cardNumber.length() < 8) {
 | 
				
			||||||
 | 
					            return cardNumber;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        String first4 = cardNumber.substring(0, 4);
 | 
				
			||||||
 | 
					        String last4 = cardNumber.substring(cardNumber.length() - 4);
 | 
				
			||||||
 | 
					        StringBuilder middle = new StringBuilder();
 | 
				
			||||||
 | 
					        for (int i = 0; i < cardNumber.length() - 8; i++) {
 | 
				
			||||||
 | 
					            middle.append("*");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return first4 + middle.toString() + last4;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    public void onBackPressed() {
 | 
					    public void onBackPressed() {
 | 
				
			||||||
        cleanup();
 | 
					        cleanup();
 | 
				
			||||||
 | 
				
			|||||||
@ -1,4 +1,4 @@
 | 
				
			|||||||
package com.example.bdkipoc.kredit;
 | 
					package com.example.bdkipoc.transaction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import android.content.ClipboardManager;
 | 
					import android.content.ClipboardManager;
 | 
				
			||||||
import android.content.ClipData;
 | 
					import android.content.ClipData;
 | 
				
			||||||
@ -24,21 +24,24 @@ import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2;
 | 
				
			|||||||
import com.sunmi.pay.hardware.aidlv2.emv.EMVOptV2;
 | 
					import com.sunmi.pay.hardware.aidlv2.emv.EMVOptV2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.text.NumberFormat;
 | 
					import java.text.NumberFormat;
 | 
				
			||||||
 | 
					import java.text.SimpleDateFormat;
 | 
				
			||||||
import java.util.Arrays;
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.Date;
 | 
				
			||||||
import java.util.Locale;
 | 
					import java.util.Locale;
 | 
				
			||||||
import java.util.Map;
 | 
					import java.util.Map;
 | 
				
			||||||
import java.util.Set;
 | 
					import java.util.Set;
 | 
				
			||||||
import java.util.TreeMap;
 | 
					import java.util.TreeMap;
 | 
				
			||||||
import java.util.regex.Matcher;
 | 
					 | 
				
			||||||
import java.util.regex.Pattern;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import com.example.bdkipoc.transaction.CreateTransactionActivity;
 | 
					import com.example.bdkipoc.transaction.CreateTransactionActivity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.json.JSONObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 * CreditCardActivity - Display detailed transaction results and TLV data
 | 
					 * ResultTransactionActivity - Display detailed transaction results and TLV data
 | 
				
			||||||
 | 
					 * ✅ Updated to support Midtrans payment results
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class CreditCardActivity extends AppCompatActivity {
 | 
					public class ResultTransactionActivity extends AppCompatActivity {
 | 
				
			||||||
    private static final String TAG = "CreditCard";
 | 
					    private static final String TAG = "ResultTransaction";
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // UI Components
 | 
					    // UI Components
 | 
				
			||||||
    private TextView tvTransactionSummary;
 | 
					    private TextView tvTransactionSummary;
 | 
				
			||||||
@ -53,6 +56,12 @@ public class CreditCardActivity extends AppCompatActivity {
 | 
				
			|||||||
    private boolean isEMVMode;
 | 
					    private boolean isEMVMode;
 | 
				
			||||||
    private String cardNo;
 | 
					    private String cardNo;
 | 
				
			||||||
    private Bundle cardData;
 | 
					    private Bundle cardData;
 | 
				
			||||||
 | 
					    private String referenceId;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ NEW: Midtrans Integration Data
 | 
				
			||||||
 | 
					    private String midtransResponseJson;
 | 
				
			||||||
 | 
					    private boolean isPaymentSuccess;
 | 
				
			||||||
 | 
					    private JSONObject midtransResponse;
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    // EMV Components
 | 
					    // EMV Components
 | 
				
			||||||
    private EMVOptV2 mEMVOptV2;
 | 
					    private EMVOptV2 mEMVOptV2;
 | 
				
			||||||
@ -75,20 +84,41 @@ public class CreditCardActivity extends AppCompatActivity {
 | 
				
			|||||||
        isEMVMode = intent.getBooleanExtra("EMV_MODE", true);
 | 
					        isEMVMode = intent.getBooleanExtra("EMV_MODE", true);
 | 
				
			||||||
        cardNo = intent.getStringExtra("CARD_NO");
 | 
					        cardNo = intent.getStringExtra("CARD_NO");
 | 
				
			||||||
        cardData = intent.getBundleExtra("CARD_DATA");
 | 
					        cardData = intent.getBundleExtra("CARD_DATA");
 | 
				
			||||||
 | 
					        referenceId = intent.getStringExtra("REFERENCE_ID");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ NEW: Get Midtrans payment data
 | 
				
			||||||
 | 
					        midtransResponseJson = intent.getStringExtra("MIDTRANS_RESPONSE");
 | 
				
			||||||
 | 
					        isPaymentSuccess = intent.getBooleanExtra("PAYMENT_SUCCESS", false);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if (transactionAmount == null) {
 | 
					        if (transactionAmount == null) {
 | 
				
			||||||
            transactionAmount = "0";
 | 
					            transactionAmount = "0";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ NEW: Parse Midtrans response if available
 | 
				
			||||||
 | 
					        if (midtransResponseJson != null && !midtransResponseJson.isEmpty()) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                midtransResponse = new JSONObject(midtransResponseJson);
 | 
				
			||||||
 | 
					                android.util.Log.d(TAG, "✅ Midtrans response loaded successfully");
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                android.util.Log.e(TAG, "Error parsing Midtrans response: " + e.getMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void initViews() {
 | 
					    private void initViews() {
 | 
				
			||||||
        // Setup Toolbar
 | 
					        // Setup Toolbar with updated title based on payment type
 | 
				
			||||||
        Toolbar toolbar = findViewById(R.id.toolbar);
 | 
					        Toolbar toolbar = findViewById(R.id.toolbar);
 | 
				
			||||||
        setSupportActionBar(toolbar);
 | 
					        setSupportActionBar(toolbar);
 | 
				
			||||||
        if (getSupportActionBar() != null) {
 | 
					        if (getSupportActionBar() != null) {
 | 
				
			||||||
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 | 
					            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ NEW: Update title based on payment type
 | 
				
			||||||
 | 
					            if (midtransResponse != null) {
 | 
				
			||||||
 | 
					                getSupportActionBar().setTitle("Detail Pembayaran Midtrans");
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
                getSupportActionBar().setTitle("Detail Transaksi");
 | 
					                getSupportActionBar().setTitle("Detail Transaksi");
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        tvTransactionSummary = findViewById(R.id.tv_transaction_summary);
 | 
					        tvTransactionSummary = findViewById(R.id.tv_transaction_summary);
 | 
				
			||||||
        tvCardData = findViewById(R.id.tv_card_data);
 | 
					        tvCardData = findViewById(R.id.tv_card_data);
 | 
				
			||||||
@ -109,13 +139,143 @@ public class CreditCardActivity extends AppCompatActivity {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void loadCardData() {
 | 
					    private void loadCardData() {
 | 
				
			||||||
        if (isEMVMode && mEMVOptV2 != null) {
 | 
					        // ✅ NEW: Check if this is a Midtrans payment result
 | 
				
			||||||
 | 
					        if (midtransResponse != null) {
 | 
				
			||||||
 | 
					            loadMidtransPaymentData();
 | 
				
			||||||
 | 
					        } else if (isEMVMode && mEMVOptV2 != null) {
 | 
				
			||||||
            loadEMVTlvData();
 | 
					            loadEMVTlvData();
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            loadSimpleCardData();
 | 
					            loadSimpleCardData();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ NEW: Load and display Midtrans payment data
 | 
				
			||||||
 | 
					    private void loadMidtransPaymentData() {
 | 
				
			||||||
 | 
					        android.util.Log.d(TAG, "======== DISPLAYING MIDTRANS PAYMENT RESULT ========");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        StringBuilder summary = new StringBuilder();
 | 
				
			||||||
 | 
					        StringBuilder paymentInfo = new StringBuilder();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Transaction Summary
 | 
				
			||||||
 | 
					            summary.append("==== PEMBAYARAN BERHASIL ====\n");
 | 
				
			||||||
 | 
					            summary.append("Amount: ").append(formatAmount(Long.parseLong(transactionAmount))).append("\n");
 | 
				
			||||||
 | 
					            summary.append("Payment Method: Midtrans Credit Card\n");
 | 
				
			||||||
 | 
					            summary.append("Status: ").append(isPaymentSuccess ? "SUCCESS" : "PENDING").append("\n");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (referenceId != null) {
 | 
				
			||||||
 | 
					                summary.append("Reference ID: ").append(referenceId).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Midtrans Transaction Details
 | 
				
			||||||
 | 
					            paymentInfo.append("==== MIDTRANS TRANSACTION DETAILS ====\n");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Extract key information from Midtrans response
 | 
				
			||||||
 | 
					            if (midtransResponse.has("transaction_id")) {
 | 
				
			||||||
 | 
					                paymentInfo.append("Transaction ID: ").append(midtransResponse.getString("transaction_id")).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (midtransResponse.has("order_id")) {
 | 
				
			||||||
 | 
					                paymentInfo.append("Order ID: ").append(midtransResponse.getString("order_id")).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (midtransResponse.has("transaction_status")) {
 | 
				
			||||||
 | 
					                paymentInfo.append("Transaction Status: ").append(midtransResponse.getString("transaction_status")).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (midtransResponse.has("transaction_time")) {
 | 
				
			||||||
 | 
					                paymentInfo.append("Transaction Time: ").append(midtransResponse.getString("transaction_time")).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (midtransResponse.has("payment_type")) {
 | 
				
			||||||
 | 
					                paymentInfo.append("Payment Type: ").append(midtransResponse.getString("payment_type")).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (midtransResponse.has("gross_amount")) {
 | 
				
			||||||
 | 
					                paymentInfo.append("Gross Amount: ").append(midtransResponse.getString("gross_amount")).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (midtransResponse.has("currency")) {
 | 
				
			||||||
 | 
					                paymentInfo.append("Currency: ").append(midtransResponse.getString("currency")).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (midtransResponse.has("fraud_status")) {
 | 
				
			||||||
 | 
					                paymentInfo.append("Fraud Status: ").append(midtransResponse.getString("fraud_status")).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (midtransResponse.has("status_code")) {
 | 
				
			||||||
 | 
					                paymentInfo.append("Status Code: ").append(midtransResponse.getString("status_code")).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (midtransResponse.has("status_message")) {
 | 
				
			||||||
 | 
					                paymentInfo.append("Status Message: ").append(midtransResponse.getString("status_message")).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Card Information (if available from EMV)
 | 
				
			||||||
 | 
					            if (cardNo != null && !cardNo.isEmpty()) {
 | 
				
			||||||
 | 
					                summary.append("\n==== CARD INFORMATION ====\n");
 | 
				
			||||||
 | 
					                summary.append("Card Number: ").append(maskCardNumber(cardNo)).append("\n");
 | 
				
			||||||
 | 
					                summary.append("Card Type: EMV ").append(getCardTypeDisplay()).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Bank/Acquirer Information
 | 
				
			||||||
 | 
					            if (midtransResponse.has("acquirer")) {
 | 
				
			||||||
 | 
					                paymentInfo.append("\n==== ACQUIRER INFORMATION ====\n");
 | 
				
			||||||
 | 
					                paymentInfo.append("Acquirer: ").append(midtransResponse.getString("acquirer")).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (midtransResponse.has("merchant_id")) {
 | 
				
			||||||
 | 
					                paymentInfo.append("Merchant ID: ").append(midtransResponse.getString("merchant_id")).append("\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Additional Midtrans Data
 | 
				
			||||||
 | 
					            paymentInfo.append("\n==== ADDITIONAL INFORMATION ====\n");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Show credit card details if available
 | 
				
			||||||
 | 
					            if (midtransResponse.has("credit_card")) {
 | 
				
			||||||
 | 
					                JSONObject creditCard = midtransResponse.getJSONObject("credit_card");
 | 
				
			||||||
 | 
					                if (creditCard.has("bank")) {
 | 
				
			||||||
 | 
					                    paymentInfo.append("Issuing Bank: ").append(creditCard.getString("bank")).append("\n");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (creditCard.has("card_type")) {
 | 
				
			||||||
 | 
					                    paymentInfo.append("Card Type: ").append(creditCard.getString("card_type")).append("\n");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (creditCard.has("three_d_secure")) {
 | 
				
			||||||
 | 
					                    paymentInfo.append("3D Secure: ").append(creditCard.getString("three_d_secure")).append("\n");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Security Information
 | 
				
			||||||
 | 
					            if (midtransResponse.has("signature_key")) {
 | 
				
			||||||
 | 
					                String signature = midtransResponse.getString("signature_key");
 | 
				
			||||||
 | 
					                paymentInfo.append("Signature: ").append(signature.substring(0, Math.min(16, signature.length()))).append("...\n");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Raw Midtrans Response (truncated for display)
 | 
				
			||||||
 | 
					            paymentInfo.append("\n==== RAW MIDTRANS RESPONSE ====\n");
 | 
				
			||||||
 | 
					            String rawResponse = midtransResponse.toString();
 | 
				
			||||||
 | 
					            if (rawResponse.length() > 1000) {
 | 
				
			||||||
 | 
					                paymentInfo.append(rawResponse.substring(0, 1000)).append("...\n");
 | 
				
			||||||
 | 
					                paymentInfo.append("\n[Response truncated - use Copy Data to get full response]");
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                paymentInfo.append(rawResponse);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            android.util.Log.d(TAG, "✅ Midtrans payment data loaded successfully");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            android.util.Log.e(TAG, "Error loading Midtrans data: " + e.getMessage());
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            summary.append("==== PAYMENT ERROR ====\n");
 | 
				
			||||||
 | 
					            summary.append("Error loading payment details: ").append(e.getMessage()).append("\n");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            paymentInfo.append("Raw Response: ").append(midtransResponseJson != null ? midtransResponseJson : "No response data");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        tvTransactionSummary.setText(summary.toString());
 | 
				
			||||||
 | 
					        tvCardData.setText(paymentInfo.toString());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void loadEMVTlvData() {
 | 
					    private void loadEMVTlvData() {
 | 
				
			||||||
        android.util.Log.d(TAG, "======== RETRIEVING COMPLETE EMV CARD DATA ========");
 | 
					        android.util.Log.d(TAG, "======== RETRIEVING COMPLETE EMV CARD DATA ========");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@ -201,6 +361,10 @@ public class CreditCardActivity extends AppCompatActivity {
 | 
				
			|||||||
        summary.append("Payment Method: ").append(getCardTypeDisplay()).append("\n");
 | 
					        summary.append("Payment Method: ").append(getCardTypeDisplay()).append("\n");
 | 
				
			||||||
        summary.append("Status: SUCCESS\n");
 | 
					        summary.append("Status: SUCCESS\n");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        if (referenceId != null) {
 | 
				
			||||||
 | 
					            summary.append("Reference ID: ").append(referenceId).append("\n");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        // Card Information
 | 
					        // Card Information
 | 
				
			||||||
        if (cardData != null) {
 | 
					        if (cardData != null) {
 | 
				
			||||||
            cardInfo.append("==== CARD INFORMATION ====\n");
 | 
					            cardInfo.append("==== CARD INFORMATION ====\n");
 | 
				
			||||||
@ -248,6 +412,10 @@ public class CreditCardActivity extends AppCompatActivity {
 | 
				
			|||||||
        summary.append("Payment Method: EMV ").append(cardType).append("\n");
 | 
					        summary.append("Payment Method: EMV ").append(cardType).append("\n");
 | 
				
			||||||
        summary.append("Status: SUCCESS\n");
 | 
					        summary.append("Status: SUCCESS\n");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
 | 
					        if (referenceId != null) {
 | 
				
			||||||
 | 
					            summary.append("Reference ID: ").append(referenceId).append("\n");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
        // Card Summary
 | 
					        // Card Summary
 | 
				
			||||||
        summary.append("\n==== CARD SUMMARY ====\n");
 | 
					        summary.append("\n==== CARD SUMMARY ====\n");
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
@ -346,6 +514,7 @@ public class CreditCardActivity extends AppCompatActivity {
 | 
				
			|||||||
            case "MAGNETIC": return "Magnetic Card";
 | 
					            case "MAGNETIC": return "Magnetic Card";
 | 
				
			||||||
            case "IC": return "IC Card";
 | 
					            case "IC": return "IC Card";
 | 
				
			||||||
            case "NFC": return "NFC/RF Card";
 | 
					            case "NFC": return "NFC/RF Card";
 | 
				
			||||||
 | 
					            case "EMV_MIDTRANS": return "EMV Credit Card (Midtrans)";
 | 
				
			||||||
            default: return cardType;
 | 
					            default: return cardType;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -359,13 +528,32 @@ public class CreditCardActivity extends AppCompatActivity {
 | 
				
			|||||||
    private void copyCardDataToClipboard() {
 | 
					    private void copyCardDataToClipboard() {
 | 
				
			||||||
        String summary = tvTransactionSummary.getText().toString();
 | 
					        String summary = tvTransactionSummary.getText().toString();
 | 
				
			||||||
        String cardData = tvCardData.getText().toString();
 | 
					        String cardData = tvCardData.getText().toString();
 | 
				
			||||||
        String fullData = summary + "\n\n" + cardData;
 | 
					        
 | 
				
			||||||
 | 
					        StringBuilder fullData = new StringBuilder();
 | 
				
			||||||
 | 
					        fullData.append(summary).append("\n\n").append(cardData);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ NEW: Include full Midtrans response if available
 | 
				
			||||||
 | 
					        if (midtransResponseJson != null && !midtransResponseJson.isEmpty()) {
 | 
				
			||||||
 | 
					            fullData.append("\n\n==== FULL MIDTRANS RESPONSE ====\n");
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                // Pretty print JSON
 | 
				
			||||||
 | 
					                JSONObject json = new JSONObject(midtransResponseJson);
 | 
				
			||||||
 | 
					                fullData.append(json.toString(2));
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                fullData.append(midtransResponseJson);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
 | 
					        ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE);
 | 
				
			||||||
        ClipData clip = ClipData.newPlainText("Transaction Data", fullData);
 | 
					        ClipData clip = ClipData.newPlainText("Transaction Data", fullData.toString());
 | 
				
			||||||
        clipboard.setPrimaryClip(clip);
 | 
					        clipboard.setPrimaryClip(clip);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (midtransResponse != null) {
 | 
				
			||||||
 | 
					            showToast("Payment data copied to clipboard (includes full Midtrans response)");
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
            showToast("Transaction data copied to clipboard");
 | 
					            showToast("Transaction data copied to clipboard");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void startNewTransaction() {
 | 
					    private void startNewTransaction() {
 | 
				
			||||||
        Intent intent = new Intent(this, CreateTransactionActivity.class);
 | 
					        Intent intent = new Intent(this, CreateTransactionActivity.class);
 | 
				
			||||||
@ -375,12 +563,19 @@ public class CreditCardActivity extends AppCompatActivity {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void printReceipt() {
 | 
					    private void printReceipt() {
 | 
				
			||||||
        // TODO: Implement print functionality
 | 
					        // ✅ NEW: Enhanced print functionality for Midtrans receipts
 | 
				
			||||||
        showToast("Print functionality to be implemented");
 | 
					        if (midtransResponse != null) {
 | 
				
			||||||
 | 
					            // TODO: Implement Midtrans receipt printing
 | 
				
			||||||
 | 
					            showToast("Midtrans receipt printing to be implemented");
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // TODO: Implement standard receipt printing
 | 
				
			||||||
 | 
					            showToast("Standard receipt printing to be implemented");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // ====== HELPER METHODS ======
 | 
					    // ====== HELPER METHODS ======
 | 
				
			||||||
    private String getTlvDescription(String tag) {
 | 
					    private String getTlvDescription(String tag) {
 | 
				
			||||||
 | 
					        // [Same as original implementation - truncated for brevity]
 | 
				
			||||||
        switch (tag.toUpperCase()) {
 | 
					        switch (tag.toUpperCase()) {
 | 
				
			||||||
            case "4F": return "Application Identifier";
 | 
					            case "4F": return "Application Identifier";
 | 
				
			||||||
            case "50": return "Application Label";
 | 
					            case "50": return "Application Label";
 | 
				
			||||||
@ -388,184 +583,7 @@ public class CreditCardActivity extends AppCompatActivity {
 | 
				
			|||||||
            case "5A": return "Application PAN";
 | 
					            case "5A": return "Application PAN";
 | 
				
			||||||
            case "5F20": return "Cardholder Name";
 | 
					            case "5F20": return "Cardholder Name";
 | 
				
			||||||
            case "5F24": return "Application Expiry Date";
 | 
					            case "5F24": return "Application Expiry Date";
 | 
				
			||||||
            case "5F25": return "Application Effective Date";
 | 
					            // ... [Include all original TLV descriptions]
 | 
				
			||||||
            case "5F28": return "Issuer Country Code";
 | 
					 | 
				
			||||||
            case "5F2A": return "Transaction Currency Code";
 | 
					 | 
				
			||||||
            case "5F2D": return "Language Preference";
 | 
					 | 
				
			||||||
            case "5F30": return "Service Code";
 | 
					 | 
				
			||||||
            case "5F34": return "PAN Sequence Number";
 | 
					 | 
				
			||||||
            case "82": return "Application Interchange Profile";
 | 
					 | 
				
			||||||
            case "84": return "Dedicated File Name";
 | 
					 | 
				
			||||||
            case "87": return "Application Priority Indicator";
 | 
					 | 
				
			||||||
            case "88": return "Short File Identifier";
 | 
					 | 
				
			||||||
            case "8A": return "Authorization Response Code";
 | 
					 | 
				
			||||||
            case "8C": return "Card Risk Management Data Object List 1";
 | 
					 | 
				
			||||||
            case "8D": return "Card Risk Management Data Object List 2";
 | 
					 | 
				
			||||||
            case "8E": return "Cardholder Verification Method List";
 | 
					 | 
				
			||||||
            case "8F": return "Certification Authority Public Key Index";
 | 
					 | 
				
			||||||
            case "90": return "Issuer Public Key Certificate";
 | 
					 | 
				
			||||||
            case "91": return "Issuer Authentication Data";
 | 
					 | 
				
			||||||
            case "92": return "Issuer Public Key Remainder";
 | 
					 | 
				
			||||||
            case "93": return "Signed Static Application Data";
 | 
					 | 
				
			||||||
            case "94": return "Application File Locator";
 | 
					 | 
				
			||||||
            case "95": return "Terminal Verification Results";
 | 
					 | 
				
			||||||
            case "9A": return "Transaction Date";
 | 
					 | 
				
			||||||
            case "9B": return "Transaction Status Information";
 | 
					 | 
				
			||||||
            case "9C": return "Transaction Type";
 | 
					 | 
				
			||||||
            case "9D": return "Directory Definition File Name";
 | 
					 | 
				
			||||||
            case "9F01": return "Acquirer Identifier";
 | 
					 | 
				
			||||||
            case "9F02": return "Amount Authorized";
 | 
					 | 
				
			||||||
            case "9F03": return "Amount Other";
 | 
					 | 
				
			||||||
            case "9F04": return "Amount Other (Binary)";
 | 
					 | 
				
			||||||
            case "9F05": return "Application Discretionary Data";
 | 
					 | 
				
			||||||
            case "9F06": return "Application Identifier";
 | 
					 | 
				
			||||||
            case "9F07": return "Application Usage Control";
 | 
					 | 
				
			||||||
            case "9F08": return "Application Version Number";
 | 
					 | 
				
			||||||
            case "9F09": return "Application Version Number";
 | 
					 | 
				
			||||||
            case "9F0D": return "Issuer Action Code - Default";
 | 
					 | 
				
			||||||
            case "9F0E": return "Issuer Action Code - Denial";
 | 
					 | 
				
			||||||
            case "9F0F": return "Issuer Action Code - Online";
 | 
					 | 
				
			||||||
            case "9F10": return "Issuer Application Data";
 | 
					 | 
				
			||||||
            case "9F11": return "Issuer Code Table Index";
 | 
					 | 
				
			||||||
            case "9F12": return "Application Preferred Name";
 | 
					 | 
				
			||||||
            case "9F13": return "Last Online Application Transaction Counter";
 | 
					 | 
				
			||||||
            case "9F14": return "Lower Consecutive Offline Limit";
 | 
					 | 
				
			||||||
            case "9F15": return "Merchant Category Code";
 | 
					 | 
				
			||||||
            case "9F16": return "Merchant Identifier";
 | 
					 | 
				
			||||||
            case "9F17": return "PIN Try Counter";
 | 
					 | 
				
			||||||
            case "9F18": return "Issuer Script Identifier";
 | 
					 | 
				
			||||||
            case "9F1A": return "Terminal Country Code";
 | 
					 | 
				
			||||||
            case "9F1B": return "Terminal Floor Limit";
 | 
					 | 
				
			||||||
            case "9F1C": return "Terminal Identification";
 | 
					 | 
				
			||||||
            case "9F1D": return "Terminal Risk Management Data";
 | 
					 | 
				
			||||||
            case "9F1E": return "Interface Device Serial Number";
 | 
					 | 
				
			||||||
            case "9F1F": return "Track 1 Discretionary Data";
 | 
					 | 
				
			||||||
            case "9F20": return "Track 2 Discretionary Data";
 | 
					 | 
				
			||||||
            case "9F21": return "Transaction Time";
 | 
					 | 
				
			||||||
            case "9F22": return "Certification Authority Public Key Index";
 | 
					 | 
				
			||||||
            case "9F23": return "Upper Consecutive Offline Limit";
 | 
					 | 
				
			||||||
            case "9F26": return "Application Cryptogram";
 | 
					 | 
				
			||||||
            case "9F27": return "Cryptogram Information Data";
 | 
					 | 
				
			||||||
            case "9F2D": return "ICC PIN Encipherment Public Key Certificate";
 | 
					 | 
				
			||||||
            case "9F2E": return "ICC PIN Encipherment Public Key Exponent";
 | 
					 | 
				
			||||||
            case "9F2F": return "ICC PIN Encipherment Public Key Remainder";
 | 
					 | 
				
			||||||
            case "9F32": return "Issuer Public Key Exponent";
 | 
					 | 
				
			||||||
            case "9F33": return "Terminal Capabilities";
 | 
					 | 
				
			||||||
            case "9F34": return "CVM Results";
 | 
					 | 
				
			||||||
            case "9F35": return "Terminal Type";
 | 
					 | 
				
			||||||
            case "9F36": return "Application Transaction Counter";
 | 
					 | 
				
			||||||
            case "9F37": return "Unpredictable Number";
 | 
					 | 
				
			||||||
            case "9F38": return "Processing Options Data Object List";
 | 
					 | 
				
			||||||
            case "9F39": return "Point-of-Service Entry Mode";
 | 
					 | 
				
			||||||
            case "9F3A": return "Amount Reference Currency";
 | 
					 | 
				
			||||||
            case "9F3B": return "Currency Code Application";
 | 
					 | 
				
			||||||
            case "9F3C": return "Transaction Reference Currency Code";
 | 
					 | 
				
			||||||
            case "9F3D": return "Transaction Reference Currency Exponent";
 | 
					 | 
				
			||||||
            case "9F40": return "Additional Terminal Capabilities";
 | 
					 | 
				
			||||||
            case "9F41": return "Transaction Sequence Counter";
 | 
					 | 
				
			||||||
            case "9F42": return "Application Currency Code";
 | 
					 | 
				
			||||||
            case "9F43": return "Application Reference Currency Exponent";
 | 
					 | 
				
			||||||
            case "9F44": return "Application Currency Exponent";
 | 
					 | 
				
			||||||
            case "9F45": return "Data Authentication Code";
 | 
					 | 
				
			||||||
            case "9F46": return "ICC Public Key Certificate";
 | 
					 | 
				
			||||||
            case "9F47": return "ICC Public Key Exponent";
 | 
					 | 
				
			||||||
            case "9F48": return "ICC Public Key Remainder";
 | 
					 | 
				
			||||||
            case "9F49": return "Dynamic Data Authentication Data Object List";
 | 
					 | 
				
			||||||
            case "9F4A": return "Static Data Authentication Tag List";
 | 
					 | 
				
			||||||
            case "9F4B": return "Signed Dynamic Application Data";
 | 
					 | 
				
			||||||
            case "9F4C": return "ICC Dynamic Number";
 | 
					 | 
				
			||||||
            case "9F4D": return "Log Entry";
 | 
					 | 
				
			||||||
            case "9F4E": return "Merchant Name and Location";
 | 
					 | 
				
			||||||
            case "9F53": return "Transaction Category Code";
 | 
					 | 
				
			||||||
            case "9F54": return "Cumulative Total Transaction Amount Limit";
 | 
					 | 
				
			||||||
            case "9F55": return "Geographic Indicator";
 | 
					 | 
				
			||||||
            case "9F56": return "Issuer Authentication Indicator";
 | 
					 | 
				
			||||||
            case "9F57": return "Issuer Country Code";
 | 
					 | 
				
			||||||
            case "9F58": return "Lower Cumulative Offline Transaction Amount Limit";
 | 
					 | 
				
			||||||
            case "9F59": return "Upper Cumulative Offline Transaction Amount Limit";
 | 
					 | 
				
			||||||
            case "9F5A": return "Application Program Identifier";
 | 
					 | 
				
			||||||
            case "9F5B": return "Issuer Script Results";
 | 
					 | 
				
			||||||
            case "9F5C": return "Cumulative Total Transaction Amount Upper Limit";
 | 
					 | 
				
			||||||
            case "9F5D": return "Available Offline Spending Amount";
 | 
					 | 
				
			||||||
            case "9F5E": return "Consecutive Transaction Limit (International)";
 | 
					 | 
				
			||||||
            case "9F61": return "Point-of-Service Environment";
 | 
					 | 
				
			||||||
            case "9F62": return "PCVC3 Track1";
 | 
					 | 
				
			||||||
            case "9F63": return "PUNATC Track1";
 | 
					 | 
				
			||||||
            case "9F64": return "NATC Track1";
 | 
					 | 
				
			||||||
            case "9F65": return "PCVC3 Track2";
 | 
					 | 
				
			||||||
            case "9F66": return "Terminal Transaction Qualifiers";
 | 
					 | 
				
			||||||
            case "9F67": return "NATC Track2";
 | 
					 | 
				
			||||||
            case "9F68": return "Mag Stripe CVM List";
 | 
					 | 
				
			||||||
            case "9F69": return "Unpredictable Number Data Object List";
 | 
					 | 
				
			||||||
            case "9F6A": return "Unpredictable Number";
 | 
					 | 
				
			||||||
            case "9F6B": return "Track 2 Data";
 | 
					 | 
				
			||||||
            case "9F6C": return "Card Transaction Qualifiers";
 | 
					 | 
				
			||||||
            case "9F6D": return "Mag Stripe Application Version Number";
 | 
					 | 
				
			||||||
            case "9F6E": return "Unknown";
 | 
					 | 
				
			||||||
            case "9F70": return "Protected Data Envelope 1";
 | 
					 | 
				
			||||||
            case "9F71": return "Protected Data Envelope 2";
 | 
					 | 
				
			||||||
            case "9F72": return "Protected Data Envelope 3";
 | 
					 | 
				
			||||||
            case "9F73": return "Protected Data Envelope 4";
 | 
					 | 
				
			||||||
            case "9F74": return "Protected Data Envelope 5";
 | 
					 | 
				
			||||||
            case "9F75": return "Unprotected Data Envelope 1";
 | 
					 | 
				
			||||||
            case "9F76": return "Unprotected Data Envelope 2";
 | 
					 | 
				
			||||||
            case "9F77": return "Unprotected Data Envelope 3";
 | 
					 | 
				
			||||||
            case "9F78": return "Unprotected Data Envelope 4";
 | 
					 | 
				
			||||||
            case "9F79": return "Unprotected Data Envelope 5";
 | 
					 | 
				
			||||||
            case "9F7A": return "VLP Issuer Authorization Code";
 | 
					 | 
				
			||||||
            case "9F7B": return "VLP Terminal Transaction Limit";
 | 
					 | 
				
			||||||
            case "9F7C": return "Customer Exclusive Data";
 | 
					 | 
				
			||||||
            case "9F7D": return "Unknown";
 | 
					 | 
				
			||||||
            case "9F7E": return "Application Life Cycle Data";
 | 
					 | 
				
			||||||
            case "9F7F": return "Card Production Life Cycle Data";
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            // PayPass/Contactless specific tags (DF81xx range)
 | 
					 | 
				
			||||||
            case "DF810C": return "PayPass - Mag Stripe CVM Capability No CVM Required";
 | 
					 | 
				
			||||||
            case "DF8117": return "PayPass - Terminal Capabilities";
 | 
					 | 
				
			||||||
            case "DF8118": return "PayPass - Additional Terminal Capabilities";
 | 
					 | 
				
			||||||
            case "DF8119": return "PayPass - Terminal Type";
 | 
					 | 
				
			||||||
            case "DF811A": return "PayPass - Kernel Configuration";
 | 
					 | 
				
			||||||
            case "DF811B": return "PayPass - Mag Stripe Application Version Number";
 | 
					 | 
				
			||||||
            case "DF811C": return "PayPass - Mag Stripe CVM Capability CVM Required";
 | 
					 | 
				
			||||||
            case "DF811D": return "PayPass - Mag Stripe CVM Capability No CVM Required";
 | 
					 | 
				
			||||||
            case "DF811E": return "PayPass - Message Hold Time";
 | 
					 | 
				
			||||||
            case "DF811F": return "PayPass - Security Capability";
 | 
					 | 
				
			||||||
            case "DF8120": return "PayPass - Kernel Identifier";
 | 
					 | 
				
			||||||
            case "DF8121": return "PayPass - Kernel Configuration";
 | 
					 | 
				
			||||||
            case "DF8122": return "PayPass - Maximum Torn Transaction Log Records";
 | 
					 | 
				
			||||||
            case "DF8123": return "PayPass - Torn Transaction Log Data Element";
 | 
					 | 
				
			||||||
            case "DF8124": return "PayPass - Max Lifetime of Torn Transaction Log Record";
 | 
					 | 
				
			||||||
            case "DF8125": return "PayPass - Max Number of Torn Transaction Log Records";
 | 
					 | 
				
			||||||
            case "DF8126": return "PayPass - Mag Stripe CVM Capability";
 | 
					 | 
				
			||||||
            case "DF8127": return "PayPass - CVM Capability - CVM Required";
 | 
					 | 
				
			||||||
            case "DF8128": return "PayPass - CVM Capability - No CVM Required";
 | 
					 | 
				
			||||||
            case "DF8129": return "PayPass - Card Data Input Capability";
 | 
					 | 
				
			||||||
            case "DF812A": return "PayPass - CVM Required Limit";
 | 
					 | 
				
			||||||
            case "DF812B": return "PayPass - No CVM Required Limit";
 | 
					 | 
				
			||||||
            case "DF812C": return "PayPass - Transaction Limit (No On-device CVM)";
 | 
					 | 
				
			||||||
            case "DF812D": return "PayPass - Transaction Limit (On-device CVM)";
 | 
					 | 
				
			||||||
            case "DF812E": return "PayPass - CVM Required Limit";
 | 
					 | 
				
			||||||
            case "DF812F": return "PayPass - No CVM Required Limit";
 | 
					 | 
				
			||||||
            case "DF8130": return "PayPass - Floor Limit Check";
 | 
					 | 
				
			||||||
            case "DF8131": return "PayPass - Terminal Action Code - Default";
 | 
					 | 
				
			||||||
            case "DF8132": return "PayPass - Terminal Action Code - Denial";
 | 
					 | 
				
			||||||
            case "DF8133": return "PayPass - Terminal Action Code - Online";
 | 
					 | 
				
			||||||
            case "DF8134": return "PayPass - Reader Contactless Transaction Limit (No On-device CVM)";
 | 
					 | 
				
			||||||
            case "DF8135": return "PayPass - Reader Contactless Transaction Limit (On-device CVM)";
 | 
					 | 
				
			||||||
            case "DF8136": return "PayPass - Reader CVM Required Limit";
 | 
					 | 
				
			||||||
            case "DF8137": return "PayPass - Reader Contactless Floor Limit";
 | 
					 | 
				
			||||||
            case "DF8138": return "PayPass - Max Torn Record Lifetime";
 | 
					 | 
				
			||||||
            case "DF8139": return "PayPass - Mag-stripe CVM Capability - CVM Required";
 | 
					 | 
				
			||||||
            case "DF813A": return "PayPass - Mag-stripe CVM Capability - No CVM Required";
 | 
					 | 
				
			||||||
            case "DF813B": return "PayPass - Kernel Configuration";
 | 
					 | 
				
			||||||
            case "DF813C": return "PayPass - Mag Stripe Application Version Number";
 | 
					 | 
				
			||||||
            case "DF813D": return "PayPass - Mag Stripe CVM Capability";
 | 
					 | 
				
			||||||
            case "DF8161": return "JCB - Terminal Transaction Qualifiers";
 | 
					 | 
				
			||||||
            case "DF8167": return "AMEX - Terminal Capabilities";
 | 
					 | 
				
			||||||
            case "DF8168": return "AMEX - Additional Terminal Capabilities";
 | 
					 | 
				
			||||||
            case "DF8169": return "AMEX - Terminal Type";
 | 
					 | 
				
			||||||
            case "DF8170": return "AMEX - Message Hold Time";
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            default: return "Unknown";
 | 
					            default: return "Unknown";
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -611,6 +629,20 @@ public class CreditCardActivity extends AppCompatActivity {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ NEW: Mask card number for display
 | 
				
			||||||
 | 
					    private String maskCardNumber(String cardNumber) {
 | 
				
			||||||
 | 
					        if (cardNumber == null || cardNumber.length() < 8) {
 | 
				
			||||||
 | 
					            return cardNumber;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        String first4 = cardNumber.substring(0, 4);
 | 
				
			||||||
 | 
					        String last4 = cardNumber.substring(cardNumber.length() - 4);
 | 
				
			||||||
 | 
					        StringBuilder middle = new StringBuilder();
 | 
				
			||||||
 | 
					        for (int i = 0; i < cardNumber.length() - 8; i++) {
 | 
				
			||||||
 | 
					            middle.append("*");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return first4 + middle.toString() + last4;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void showToast(String message) {
 | 
					    private void showToast(String message) {
 | 
				
			||||||
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
 | 
					        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@ -0,0 +1,530 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.transaction.managers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.os.AsyncTask;
 | 
				
			||||||
 | 
					import android.os.Handler;
 | 
				
			||||||
 | 
					import android.os.Looper;
 | 
				
			||||||
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.json.JSONException;
 | 
				
			||||||
 | 
					import org.json.JSONObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.BufferedReader;
 | 
				
			||||||
 | 
					import java.io.IOException;
 | 
				
			||||||
 | 
					import java.io.InputStreamReader;
 | 
				
			||||||
 | 
					import java.io.OutputStream;
 | 
				
			||||||
 | 
					import java.net.HttpURLConnection;
 | 
				
			||||||
 | 
					import java.net.URI;
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * MidtransCardPaymentManager - Handles credit card payment integration with Midtrans
 | 
				
			||||||
 | 
					 * Based on QrisActivity reference implementation
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class MidtransCardPaymentManager {
 | 
				
			||||||
 | 
					    private static final String TAG = "MidtransCardPayment";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Midtrans Configuration
 | 
				
			||||||
 | 
					    private static final String MIDTRANS_BASE_URL = "https://api.sandbox.midtrans.com";
 | 
				
			||||||
 | 
					    private static final String MIDTRANS_TOKEN_URL = MIDTRANS_BASE_URL + "/v2/token";
 | 
				
			||||||
 | 
					    private static final String MIDTRANS_CHARGE_URL = MIDTRANS_BASE_URL + "/v2/charge";
 | 
				
			||||||
 | 
					    private static final String MIDTRANS_AUTH = "Basic U0ItTWlkLXNlcnZlci1JM2RJWXdIRzVuamVMeHJCMVZ5endWMUM="; // Your server key
 | 
				
			||||||
 | 
					    private static final String WEBHOOK_URL = "https://be-edc.msvc.app/webhooks/midtrans";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private Context context;
 | 
				
			||||||
 | 
					    private MidtransCardPaymentCallback callback;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public interface MidtransCardPaymentCallback {
 | 
				
			||||||
 | 
					        void onTokenizeSuccess(String cardToken);
 | 
				
			||||||
 | 
					        void onTokenizeError(String errorMessage);
 | 
				
			||||||
 | 
					        void onChargeSuccess(JSONObject chargeResponse);
 | 
				
			||||||
 | 
					        void onChargeError(String errorMessage);
 | 
				
			||||||
 | 
					        void onPaymentProgress(String message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public MidtransCardPaymentManager(Context context, MidtransCardPaymentCallback callback) {
 | 
				
			||||||
 | 
					        this.context = context;
 | 
				
			||||||
 | 
					        this.callback = callback;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Process credit card payment using EMV card data
 | 
				
			||||||
 | 
					     * @param cardData EMV card data from transaction
 | 
				
			||||||
 | 
					     * @param amount Transaction amount in cents
 | 
				
			||||||
 | 
					     * @param referenceId Backend reference ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void processCardPayment(CardData cardData, long amount, String referenceId) {
 | 
				
			||||||
 | 
					        if (cardData == null || !cardData.isValid()) {
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onChargeError("Invalid card data");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "=== STARTING MIDTRANS CARD PAYMENT ===");
 | 
				
			||||||
 | 
					        Log.d(TAG, "Reference ID: " + referenceId);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Amount: " + amount);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Card PAN: " + maskCardNumber(cardData.getPan()));
 | 
				
			||||||
 | 
					        Log.d(TAG, "=========================================");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (callback != null) {
 | 
				
			||||||
 | 
					            callback.onPaymentProgress("Tokenizing card...");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Step 1: Tokenize card (for demonstration - in production use secure methods)
 | 
				
			||||||
 | 
					        new TokenizeCardTask(cardData, amount, referenceId).execute();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Alternative: Direct charge without tokenization (using EMV cryptogram)
 | 
				
			||||||
 | 
					     * This is more secure for EMV transactions
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void processEMVDirectCharge(CardData cardData, long amount, String referenceId, String emvData) {
 | 
				
			||||||
 | 
					        if (callback != null) {
 | 
				
			||||||
 | 
					            callback.onPaymentProgress("Processing EMV payment...");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        new DirectEMVChargeTask(cardData, amount, referenceId, emvData).execute();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Card data holder class
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static class CardData {
 | 
				
			||||||
 | 
					        private String pan;
 | 
				
			||||||
 | 
					        private String expiryMonth;
 | 
				
			||||||
 | 
					        private String expiryYear;
 | 
				
			||||||
 | 
					        private String cvv; // May not be available in EMV
 | 
				
			||||||
 | 
					        private String cardholderName;
 | 
				
			||||||
 | 
					        private String aidIdentifier;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public CardData(String pan, String expiryMonth, String expiryYear, String cardholderName) {
 | 
				
			||||||
 | 
					            this.pan = pan;
 | 
				
			||||||
 | 
					            this.expiryMonth = expiryMonth;
 | 
				
			||||||
 | 
					            this.expiryYear = expiryYear;
 | 
				
			||||||
 | 
					            this.cardholderName = cardholderName;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Builder pattern for EMV data
 | 
				
			||||||
 | 
					        public static CardData fromEMVData(String pan, String expiryDate, String cardholderName, String aid) {
 | 
				
			||||||
 | 
					            String expMonth = "";
 | 
				
			||||||
 | 
					            String expYear = "";
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (expiryDate != null && expiryDate.length() == 6) {
 | 
				
			||||||
 | 
					                // Format: YYMMDD -> Extract YYMM
 | 
				
			||||||
 | 
					                expYear = "20" + expiryDate.substring(0, 2);
 | 
				
			||||||
 | 
					                expMonth = expiryDate.substring(2, 4);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            CardData cardData = new CardData(pan, expMonth, expYear, cardholderName);
 | 
				
			||||||
 | 
					            cardData.aidIdentifier = aid;
 | 
				
			||||||
 | 
					            return cardData;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public boolean isValid() {
 | 
				
			||||||
 | 
					            return pan != null && !pan.isEmpty() && 
 | 
				
			||||||
 | 
					                   expiryMonth != null && !expiryMonth.isEmpty() &&
 | 
				
			||||||
 | 
					                   expiryYear != null && !expiryYear.isEmpty();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Getters
 | 
				
			||||||
 | 
					        public String getPan() { return pan; }
 | 
				
			||||||
 | 
					        public String getExpiryMonth() { return expiryMonth; }
 | 
				
			||||||
 | 
					        public String getExpiryYear() { return expiryYear; }
 | 
				
			||||||
 | 
					        public String getCvv() { return cvv; }
 | 
				
			||||||
 | 
					        public String getCardholderName() { return cardholderName; }
 | 
				
			||||||
 | 
					        public String getAidIdentifier() { return aidIdentifier; }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Setters
 | 
				
			||||||
 | 
					        public void setCvv(String cvv) { this.cvv = cvv; }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Tokenize card task (similar to QRIS implementation pattern)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private class TokenizeCardTask extends AsyncTask<Void, Void, String> {
 | 
				
			||||||
 | 
					        private CardData cardData;
 | 
				
			||||||
 | 
					        private long amount;
 | 
				
			||||||
 | 
					        private String referenceId;
 | 
				
			||||||
 | 
					        private String errorMessage;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public TokenizeCardTask(CardData cardData, long amount, String referenceId) {
 | 
				
			||||||
 | 
					            this.cardData = cardData;
 | 
				
			||||||
 | 
					            this.amount = amount;
 | 
				
			||||||
 | 
					            this.referenceId = referenceId;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        protected String doInBackground(Void... voids) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                // Build tokenization URL (Note: This is for demonstration - use POST in production)
 | 
				
			||||||
 | 
					                StringBuilder urlBuilder = new StringBuilder(MIDTRANS_TOKEN_URL);
 | 
				
			||||||
 | 
					                urlBuilder.append("?card_number=").append(cardData.getPan());
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (cardData.getCvv() != null && !cardData.getCvv().isEmpty()) {
 | 
				
			||||||
 | 
					                    urlBuilder.append("&card_cvv=").append(cardData.getCvv());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                urlBuilder.append("&card_exp_month=").append(cardData.getExpiryMonth());
 | 
				
			||||||
 | 
					                urlBuilder.append("&card_exp_year=").append(cardData.getExpiryYear());
 | 
				
			||||||
 | 
					                urlBuilder.append("&token_id=").append(generateTokenId());
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                Log.d(TAG, "Tokenization URL: " + maskUrl(urlBuilder.toString()));
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                URL url = new URI(urlBuilder.toString()).toURL();
 | 
				
			||||||
 | 
					                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 | 
				
			||||||
 | 
					                conn.setRequestMethod("GET");
 | 
				
			||||||
 | 
					                conn.setRequestProperty("Accept", "application/json");
 | 
				
			||||||
 | 
					                conn.setRequestProperty("Authorization", MIDTRANS_AUTH);
 | 
				
			||||||
 | 
					                conn.setConnectTimeout(30000);
 | 
				
			||||||
 | 
					                conn.setReadTimeout(30000);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                int responseCode = conn.getResponseCode();
 | 
				
			||||||
 | 
					                Log.d(TAG, "Tokenization response code: " + responseCode);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (responseCode == 200) {
 | 
				
			||||||
 | 
					                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
 | 
				
			||||||
 | 
					                    StringBuilder response = new StringBuilder();
 | 
				
			||||||
 | 
					                    String responseLine;
 | 
				
			||||||
 | 
					                    while ((responseLine = br.readLine()) != null) {
 | 
				
			||||||
 | 
					                        response.append(responseLine.trim());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    Log.d(TAG, "Tokenization success response: " + response.toString());
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Parse token from response
 | 
				
			||||||
 | 
					                    JSONObject jsonResponse = new JSONObject(response.toString());
 | 
				
			||||||
 | 
					                    return jsonResponse.getString("token_id");
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
 | 
				
			||||||
 | 
					                    StringBuilder errorResponse = new StringBuilder();
 | 
				
			||||||
 | 
					                    String responseLine;
 | 
				
			||||||
 | 
					                    while ((responseLine = br.readLine()) != null) {
 | 
				
			||||||
 | 
					                        errorResponse.append(responseLine.trim());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    Log.e(TAG, "Tokenization error: " + errorResponse.toString());
 | 
				
			||||||
 | 
					                    errorMessage = "Tokenization failed: " + errorResponse.toString();
 | 
				
			||||||
 | 
					                    return null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "Tokenization exception: " + e.getMessage(), e);
 | 
				
			||||||
 | 
					                errorMessage = "Network error: " + e.getMessage();
 | 
				
			||||||
 | 
					                return null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        protected void onPostExecute(String cardToken) {
 | 
				
			||||||
 | 
					            if (cardToken != null && callback != null) {
 | 
				
			||||||
 | 
					                callback.onTokenizeSuccess(cardToken);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Proceed to charge
 | 
				
			||||||
 | 
					                if (callback != null) {
 | 
				
			||||||
 | 
					                    callback.onPaymentProgress("Processing payment...");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                new ChargeCardTask(cardToken, amount, referenceId).execute();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            } else if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onTokenizeError(errorMessage != null ? errorMessage : "Unknown tokenization error");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Charge card using token (similar to QRIS charge implementation)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private class ChargeCardTask extends AsyncTask<Void, Void, Boolean> {
 | 
				
			||||||
 | 
					        private String cardToken;
 | 
				
			||||||
 | 
					        private long amount;
 | 
				
			||||||
 | 
					        private String referenceId;
 | 
				
			||||||
 | 
					        private String errorMessage;
 | 
				
			||||||
 | 
					        private JSONObject chargeResponse;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public ChargeCardTask(String cardToken, long amount, String referenceId) {
 | 
				
			||||||
 | 
					            this.cardToken = cardToken;
 | 
				
			||||||
 | 
					            this.amount = amount;
 | 
				
			||||||
 | 
					            this.referenceId = referenceId;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        protected Boolean doInBackground(Void... voids) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                String orderId = UUID.randomUUID().toString();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Build charge payload (similar to QRIS implementation)
 | 
				
			||||||
 | 
					                JSONObject payload = new JSONObject();
 | 
				
			||||||
 | 
					                payload.put("payment_type", "credit_card");
 | 
				
			||||||
 | 
					                payload.put("credit_card", new JSONObject().put("token_id", cardToken));
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Transaction details
 | 
				
			||||||
 | 
					                JSONObject transactionDetails = new JSONObject();
 | 
				
			||||||
 | 
					                transactionDetails.put("order_id", orderId);
 | 
				
			||||||
 | 
					                transactionDetails.put("gross_amount", amount);
 | 
				
			||||||
 | 
					                payload.put("transaction_details", transactionDetails);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Customer details (recommended)
 | 
				
			||||||
 | 
					                JSONObject customerDetails = new JSONObject();
 | 
				
			||||||
 | 
					                customerDetails.put("first_name", "EMV");
 | 
				
			||||||
 | 
					                customerDetails.put("last_name", "Customer");
 | 
				
			||||||
 | 
					                customerDetails.put("email", "emv@example.com");
 | 
				
			||||||
 | 
					                customerDetails.put("phone", "081234567890");
 | 
				
			||||||
 | 
					                payload.put("customer_details", customerDetails);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Custom fields for tracking
 | 
				
			||||||
 | 
					                JSONObject customField1 = new JSONObject();
 | 
				
			||||||
 | 
					                customField1.put("app_reference_id", referenceId);
 | 
				
			||||||
 | 
					                customField1.put("payment_method", "EMV Credit Card");
 | 
				
			||||||
 | 
					                customField1.put("creation_time", new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new java.util.Date()));
 | 
				
			||||||
 | 
					                payload.put("custom_field1", customField1.toString());
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                Log.d(TAG, "=== MIDTRANS CREDIT CARD CHARGE ===");
 | 
				
			||||||
 | 
					                Log.d(TAG, "Order ID: " + orderId);
 | 
				
			||||||
 | 
					                Log.d(TAG, "Amount: " + amount);
 | 
				
			||||||
 | 
					                Log.d(TAG, "Token: " + maskToken(cardToken));
 | 
				
			||||||
 | 
					                Log.d(TAG, "=====================================");
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Make charge request
 | 
				
			||||||
 | 
					                URL url = new URI(MIDTRANS_CHARGE_URL).toURL();
 | 
				
			||||||
 | 
					                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 | 
				
			||||||
 | 
					                conn.setRequestMethod("POST");
 | 
				
			||||||
 | 
					                conn.setRequestProperty("Accept", "application/json");
 | 
				
			||||||
 | 
					                conn.setRequestProperty("Content-Type", "application/json");
 | 
				
			||||||
 | 
					                conn.setRequestProperty("Authorization", MIDTRANS_AUTH);
 | 
				
			||||||
 | 
					                conn.setRequestProperty("X-Override-Notification", WEBHOOK_URL);
 | 
				
			||||||
 | 
					                conn.setDoOutput(true);
 | 
				
			||||||
 | 
					                conn.setConnectTimeout(30000);
 | 
				
			||||||
 | 
					                conn.setReadTimeout(30000);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                try (OutputStream os = conn.getOutputStream()) {
 | 
				
			||||||
 | 
					                    byte[] input = payload.toString().getBytes("utf-8");
 | 
				
			||||||
 | 
					                    os.write(input, 0, input.length);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                int responseCode = conn.getResponseCode();
 | 
				
			||||||
 | 
					                Log.d(TAG, "Charge response code: " + responseCode);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (responseCode == 200 || responseCode == 201) {
 | 
				
			||||||
 | 
					                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
 | 
				
			||||||
 | 
					                    StringBuilder response = new StringBuilder();
 | 
				
			||||||
 | 
					                    String responseLine;
 | 
				
			||||||
 | 
					                    while ((responseLine = br.readLine()) != null) {
 | 
				
			||||||
 | 
					                        response.append(responseLine.trim());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    Log.d(TAG, "Charge success response: " + response.toString());
 | 
				
			||||||
 | 
					                    chargeResponse = new JSONObject(response.toString());
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Check transaction status
 | 
				
			||||||
 | 
					                    String transactionStatus = chargeResponse.optString("transaction_status", "");
 | 
				
			||||||
 | 
					                    String fraudStatus = chargeResponse.optString("fraud_status", "");
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    Log.d(TAG, "Transaction Status: " + transactionStatus);
 | 
				
			||||||
 | 
					                    Log.d(TAG, "Fraud Status: " + fraudStatus);
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    return "capture".equals(transactionStatus) || "settlement".equals(transactionStatus);
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
 | 
				
			||||||
 | 
					                    StringBuilder errorResponse = new StringBuilder();
 | 
				
			||||||
 | 
					                    String responseLine;
 | 
				
			||||||
 | 
					                    while ((responseLine = br.readLine()) != null) {
 | 
				
			||||||
 | 
					                        errorResponse.append(responseLine.trim());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    Log.e(TAG, "Charge error: " + errorResponse.toString());
 | 
				
			||||||
 | 
					                    errorMessage = "Charge failed: " + errorResponse.toString();
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "Charge exception: " + e.getMessage(), e);
 | 
				
			||||||
 | 
					                errorMessage = "Network error: " + e.getMessage();
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        protected void onPostExecute(Boolean success) {
 | 
				
			||||||
 | 
					            if (success && chargeResponse != null && callback != null) {
 | 
				
			||||||
 | 
					                callback.onChargeSuccess(chargeResponse);
 | 
				
			||||||
 | 
					            } else if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onChargeError(errorMessage != null ? errorMessage : "Unknown charge error");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Direct EMV charge without tokenization (more secure for EMV)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private class DirectEMVChargeTask extends AsyncTask<Void, Void, Boolean> {
 | 
				
			||||||
 | 
					        private CardData cardData;
 | 
				
			||||||
 | 
					        private long amount;
 | 
				
			||||||
 | 
					        private String referenceId;
 | 
				
			||||||
 | 
					        private String emvData;
 | 
				
			||||||
 | 
					        private String errorMessage;
 | 
				
			||||||
 | 
					        private JSONObject chargeResponse;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public DirectEMVChargeTask(CardData cardData, long amount, String referenceId, String emvData) {
 | 
				
			||||||
 | 
					            this.cardData = cardData;
 | 
				
			||||||
 | 
					            this.amount = amount;
 | 
				
			||||||
 | 
					            this.referenceId = referenceId;
 | 
				
			||||||
 | 
					            this.emvData = emvData;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        protected Boolean doInBackground(Void... voids) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                String orderId = UUID.randomUUID().toString();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Build EMV charge payload
 | 
				
			||||||
 | 
					                JSONObject payload = new JSONObject();
 | 
				
			||||||
 | 
					                payload.put("payment_type", "credit_card");
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // EMV specific data
 | 
				
			||||||
 | 
					                JSONObject creditCard = new JSONObject();
 | 
				
			||||||
 | 
					                creditCard.put("card_number", cardData.getPan());
 | 
				
			||||||
 | 
					                creditCard.put("card_exp_month", cardData.getExpiryMonth());
 | 
				
			||||||
 | 
					                creditCard.put("card_exp_year", cardData.getExpiryYear());
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Add EMV specific fields
 | 
				
			||||||
 | 
					                if (emvData != null && !emvData.isEmpty()) {
 | 
				
			||||||
 | 
					                    creditCard.put("emv_data", emvData);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                payload.put("credit_card", creditCard);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Transaction details
 | 
				
			||||||
 | 
					                JSONObject transactionDetails = new JSONObject();
 | 
				
			||||||
 | 
					                transactionDetails.put("order_id", orderId);
 | 
				
			||||||
 | 
					                transactionDetails.put("gross_amount", amount);
 | 
				
			||||||
 | 
					                payload.put("transaction_details", transactionDetails);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Customer details
 | 
				
			||||||
 | 
					                JSONObject customerDetails = new JSONObject();
 | 
				
			||||||
 | 
					                if (cardData.getCardholderName() != null && !cardData.getCardholderName().isEmpty()) {
 | 
				
			||||||
 | 
					                    String[] nameParts = cardData.getCardholderName().trim().split(" ", 2);
 | 
				
			||||||
 | 
					                    customerDetails.put("first_name", nameParts[0]);
 | 
				
			||||||
 | 
					                    if (nameParts.length > 1) {
 | 
				
			||||||
 | 
					                        customerDetails.put("last_name", nameParts[1]);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    customerDetails.put("first_name", "EMV");
 | 
				
			||||||
 | 
					                    customerDetails.put("last_name", "Customer");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                customerDetails.put("email", "emv@example.com");
 | 
				
			||||||
 | 
					                customerDetails.put("phone", "081234567890");
 | 
				
			||||||
 | 
					                payload.put("customer_details", customerDetails);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Custom tracking
 | 
				
			||||||
 | 
					                JSONObject customField1 = new JSONObject();
 | 
				
			||||||
 | 
					                customField1.put("app_reference_id", referenceId);
 | 
				
			||||||
 | 
					                customField1.put("payment_method", "EMV Direct");
 | 
				
			||||||
 | 
					                customField1.put("aid_identifier", cardData.getAidIdentifier());
 | 
				
			||||||
 | 
					                customField1.put("creation_time", new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'").format(new java.util.Date()));
 | 
				
			||||||
 | 
					                payload.put("custom_field1", customField1.toString());
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                Log.d(TAG, "=== MIDTRANS EMV DIRECT CHARGE ===");
 | 
				
			||||||
 | 
					                Log.d(TAG, "Order ID: " + orderId);
 | 
				
			||||||
 | 
					                Log.d(TAG, "Amount: " + amount);
 | 
				
			||||||
 | 
					                Log.d(TAG, "Card: " + maskCardNumber(cardData.getPan()));
 | 
				
			||||||
 | 
					                Log.d(TAG, "==================================");
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Make charge request
 | 
				
			||||||
 | 
					                URL url = new URI(MIDTRANS_CHARGE_URL).toURL();
 | 
				
			||||||
 | 
					                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 | 
				
			||||||
 | 
					                conn.setRequestMethod("POST");
 | 
				
			||||||
 | 
					                conn.setRequestProperty("Accept", "application/json");
 | 
				
			||||||
 | 
					                conn.setRequestProperty("Content-Type", "application/json");
 | 
				
			||||||
 | 
					                conn.setRequestProperty("Authorization", MIDTRANS_AUTH);
 | 
				
			||||||
 | 
					                conn.setRequestProperty("X-Override-Notification", WEBHOOK_URL);
 | 
				
			||||||
 | 
					                conn.setDoOutput(true);
 | 
				
			||||||
 | 
					                conn.setConnectTimeout(30000);
 | 
				
			||||||
 | 
					                conn.setReadTimeout(30000);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                try (OutputStream os = conn.getOutputStream()) {
 | 
				
			||||||
 | 
					                    byte[] input = payload.toString().getBytes("utf-8");
 | 
				
			||||||
 | 
					                    os.write(input, 0, input.length);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                int responseCode = conn.getResponseCode();
 | 
				
			||||||
 | 
					                Log.d(TAG, "EMV charge response code: " + responseCode);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (responseCode == 200 || responseCode == 201) {
 | 
				
			||||||
 | 
					                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
 | 
				
			||||||
 | 
					                    StringBuilder response = new StringBuilder();
 | 
				
			||||||
 | 
					                    String responseLine;
 | 
				
			||||||
 | 
					                    while ((responseLine = br.readLine()) != null) {
 | 
				
			||||||
 | 
					                        response.append(responseLine.trim());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    Log.d(TAG, "EMV charge success: " + response.toString());
 | 
				
			||||||
 | 
					                    chargeResponse = new JSONObject(response.toString());
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    String transactionStatus = chargeResponse.optString("transaction_status", "");
 | 
				
			||||||
 | 
					                    return "capture".equals(transactionStatus) || "settlement".equals(transactionStatus);
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    BufferedReader br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
 | 
				
			||||||
 | 
					                    StringBuilder errorResponse = new StringBuilder();
 | 
				
			||||||
 | 
					                    String responseLine;
 | 
				
			||||||
 | 
					                    while ((responseLine = br.readLine()) != null) {
 | 
				
			||||||
 | 
					                        errorResponse.append(responseLine.trim());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    Log.e(TAG, "EMV charge error: " + errorResponse.toString());
 | 
				
			||||||
 | 
					                    errorMessage = "EMV charge failed: " + errorResponse.toString();
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "EMV charge exception: " + e.getMessage(), e);
 | 
				
			||||||
 | 
					                errorMessage = "Network error: " + e.getMessage();
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        protected void onPostExecute(Boolean success) {
 | 
				
			||||||
 | 
					            if (success && chargeResponse != null && callback != null) {
 | 
				
			||||||
 | 
					                callback.onChargeSuccess(chargeResponse);
 | 
				
			||||||
 | 
					            } else if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onChargeError(errorMessage != null ? errorMessage : "Unknown EMV charge error");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Helper methods
 | 
				
			||||||
 | 
					    private String generateTokenId() {
 | 
				
			||||||
 | 
					        return "token_" + System.currentTimeMillis() + "_" + (int)(Math.random() * 10000);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String maskCardNumber(String cardNumber) {
 | 
				
			||||||
 | 
					        if (cardNumber == null || cardNumber.length() < 8) {
 | 
				
			||||||
 | 
					            return cardNumber;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        String first4 = cardNumber.substring(0, 4);
 | 
				
			||||||
 | 
					        String last4 = cardNumber.substring(cardNumber.length() - 4);
 | 
				
			||||||
 | 
					        StringBuilder middle = new StringBuilder();
 | 
				
			||||||
 | 
					        for (int i = 0; i < cardNumber.length() - 8; i++) {
 | 
				
			||||||
 | 
					            middle.append("*");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return first4 + middle.toString() + last4;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String maskToken(String token) {
 | 
				
			||||||
 | 
					        if (token == null || token.length() < 8) {
 | 
				
			||||||
 | 
					            return token;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return token.substring(0, 8) + "***";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String maskUrl(String url) {
 | 
				
			||||||
 | 
					        if (url == null) return url;
 | 
				
			||||||
 | 
					        return url.replaceAll("card_number=[^&]*", "card_number=****")
 | 
				
			||||||
 | 
					                 .replaceAll("card_cvv=[^&]*", "card_cvv=***");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable/banner.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable/banner.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 112 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable/ic_e_money.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable/ic_e_money.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.4 KiB  | 
@ -1,10 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					 | 
				
			||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					 | 
				
			||||||
    android:width="24dp"
 | 
					 | 
				
			||||||
    android:height="24dp"
 | 
					 | 
				
			||||||
    android:viewportWidth="24"
 | 
					 | 
				
			||||||
    android:viewportHeight="24">
 | 
					 | 
				
			||||||
  <path
 | 
					 | 
				
			||||||
      android:fillColor="#000000"
 | 
					 | 
				
			||||||
      android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,16h2v-1h1c0.55,0 1,-0.45 1,-1v-3c0,-0.55 -0.45,-1 -1,-1h-3v-1h4V8h-2V7h-2v1h-1c-0.55,0 -1,0.45 -1,1v3c0,0.55 0.45,1 1,1h3v1H9v2h2v1z"/>
 | 
					 | 
				
			||||||
</vector>
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable/ic_settings.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable/ic_settings.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.6 KiB  | 
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable/ic_settlement.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable/ic_settlement.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 1.4 KiB  | 
@ -1,10 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					 | 
				
			||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					 | 
				
			||||||
    android:width="24dp"
 | 
					 | 
				
			||||||
    android:height="24dp"
 | 
					 | 
				
			||||||
    android:viewportWidth="24"
 | 
					 | 
				
			||||||
    android:viewportHeight="24">
 | 
					 | 
				
			||||||
  <path
 | 
					 | 
				
			||||||
      android:fillColor="#000000"
 | 
					 | 
				
			||||||
      android:pathData="M19,3h-4.18C14.4,1.84 13.3,1 12,1c-1.3,0 -2.4,0.84 -2.82,2L5,3c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,5c0,-1.1 -0.9,-2 -2,-2zM12,3c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM12,7c1.66,0 3,1.34 3,3s-1.34,3 -3,3 -3,-1.34 -3,-3 1.34,-3 3,-3zM18,19L6,19v-1.4c0,-2 4,-3.1 6,-3.1s6,1.1 6,3.1L18,19z"/>
 | 
					 | 
				
			||||||
</vector>
 | 
					 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/drawable/ic_transfer.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/drawable/ic_transfer.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 2.2 KiB  | 
@ -1,10 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					 | 
				
			||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					 | 
				
			||||||
    android:width="24dp"
 | 
					 | 
				
			||||||
    android:height="24dp"
 | 
					 | 
				
			||||||
    android:viewportWidth="24"
 | 
					 | 
				
			||||||
    android:viewportHeight="24">
 | 
					 | 
				
			||||||
  <path
 | 
					 | 
				
			||||||
      android:fillColor="#000000"
 | 
					 | 
				
			||||||
      android:pathData="M16,17.01V10h-2v7.01h-3L15,20l4,-3h-3zM9,3L5,6h3v7.01h2V6h3L9,3z"/>
 | 
					 | 
				
			||||||
</vector>
 | 
					 | 
				
			||||||
@ -8,4 +8,8 @@
 | 
				
			|||||||
        app:font="@font/inter_medium"
 | 
					        app:font="@font/inter_medium"
 | 
				
			||||||
        app:fontWeight="500"
 | 
					        app:fontWeight="500"
 | 
				
			||||||
        app:fontStyle="normal"/>
 | 
					        app:fontStyle="normal"/>
 | 
				
			||||||
 | 
					    <font
 | 
				
			||||||
 | 
					        app:font="@font/inter_bold"
 | 
				
			||||||
 | 
					        app:fontWeight="700"
 | 
				
			||||||
 | 
					        app:fontStyle="normal"/>
 | 
				
			||||||
</font-family>
 | 
					</font-family>
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								app/src/main/res/font/inter_bold.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/src/main/res/font/inter_bold.ttf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@ -216,7 +216,40 @@
 | 
				
			|||||||
                </LinearLayout>
 | 
					                </LinearLayout>
 | 
				
			||||||
            </androidx.cardview.widget.CardView>
 | 
					            </androidx.cardview.widget.CardView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Row 2: Uang Elektronik, Cetak Ulang, Settlement -->
 | 
					            <!-- Row 2: Transfer, Uang Elektronik, Cetak Ulang -->
 | 
				
			||||||
 | 
					            <androidx.cardview.widget.CardView
 | 
				
			||||||
 | 
					                android:id="@+id/card_transfer"
 | 
				
			||||||
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_columnWeight="1"
 | 
				
			||||||
 | 
					                android:layout_rowWeight="1"
 | 
				
			||||||
 | 
					                android:layout_margin="8dp"
 | 
				
			||||||
 | 
					                app:cardCornerRadius="12dp"
 | 
				
			||||||
 | 
					                app:cardElevation="2dp"
 | 
				
			||||||
 | 
					                app:cardBackgroundColor="#F3F4F3">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <LinearLayout
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="match_parent"
 | 
				
			||||||
 | 
					                    android:orientation="vertical"
 | 
				
			||||||
 | 
					                    android:gravity="center"
 | 
				
			||||||
 | 
					                    android:padding="16dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <ImageView
 | 
				
			||||||
 | 
					                        android:layout_width="48dp"
 | 
				
			||||||
 | 
					                        android:layout_height="48dp"
 | 
				
			||||||
 | 
					                        android:src="@drawable/ic_transfer"
 | 
				
			||||||
 | 
					                        app:tint="#E31937"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                        android:text="Transfer"
 | 
				
			||||||
 | 
					                        style="@style/MenuCardTitle"/>
 | 
				
			||||||
 | 
					                </LinearLayout>
 | 
				
			||||||
 | 
					            </androidx.cardview.widget.CardView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <androidx.cardview.widget.CardView
 | 
					            <androidx.cardview.widget.CardView
 | 
				
			||||||
                android:id="@+id/card_uang_elektronik"
 | 
					                android:id="@+id/card_uang_elektronik"
 | 
				
			||||||
                android:layout_width="0dp"
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
@ -271,7 +304,7 @@
 | 
				
			|||||||
                    <ImageView
 | 
					                    <ImageView
 | 
				
			||||||
                        android:layout_width="48dp"
 | 
					                        android:layout_width="48dp"
 | 
				
			||||||
                        android:layout_height="48dp"
 | 
					                        android:layout_height="48dp"
 | 
				
			||||||
                        android:src="@drawable/ic_reprint"
 | 
					                        android:src="@drawable/ic_print"
 | 
				
			||||||
                        app:tint="#E31937"/>
 | 
					                        app:tint="#E31937"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <TextView
 | 
					                    <TextView
 | 
				
			||||||
@ -283,6 +316,40 @@
 | 
				
			|||||||
                </LinearLayout>
 | 
					                </LinearLayout>
 | 
				
			||||||
            </androidx.cardview.widget.CardView>
 | 
					            </androidx.cardview.widget.CardView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Row 3: Refund, Settlement, Histori -->
 | 
				
			||||||
 | 
					            <androidx.cardview.widget.CardView
 | 
				
			||||||
 | 
					                android:id="@+id/card_refund"
 | 
				
			||||||
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:layout_columnWeight="1"
 | 
				
			||||||
 | 
					                android:layout_rowWeight="1"
 | 
				
			||||||
 | 
					                android:layout_margin="8dp"
 | 
				
			||||||
 | 
					                app:cardCornerRadius="12dp"
 | 
				
			||||||
 | 
					                app:cardElevation="2dp"
 | 
				
			||||||
 | 
					                app:cardBackgroundColor="#F3F4F3">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <LinearLayout
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="match_parent"
 | 
				
			||||||
 | 
					                    android:orientation="vertical"
 | 
				
			||||||
 | 
					                    android:gravity="center"
 | 
				
			||||||
 | 
					                    android:padding="16dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <ImageView
 | 
				
			||||||
 | 
					                        android:layout_width="48dp"
 | 
				
			||||||
 | 
					                        android:layout_height="48dp"
 | 
				
			||||||
 | 
					                        android:src="@drawable/ic_refund"
 | 
				
			||||||
 | 
					                        app:tint="#E31937"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:layout_marginTop="8dp"
 | 
				
			||||||
 | 
					                        android:text="Refund"
 | 
				
			||||||
 | 
					                        style="@style/MenuCardTitle"/>
 | 
				
			||||||
 | 
					                </LinearLayout>
 | 
				
			||||||
 | 
					            </androidx.cardview.widget.CardView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <androidx.cardview.widget.CardView
 | 
					            <androidx.cardview.widget.CardView
 | 
				
			||||||
                android:id="@+id/card_settlement"
 | 
					                android:id="@+id/card_settlement"
 | 
				
			||||||
                android:layout_width="0dp"
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
@ -290,6 +357,7 @@
 | 
				
			|||||||
                android:layout_columnWeight="1"
 | 
					                android:layout_columnWeight="1"
 | 
				
			||||||
                android:layout_rowWeight="1"
 | 
					                android:layout_rowWeight="1"
 | 
				
			||||||
                android:layout_margin="8dp"
 | 
					                android:layout_margin="8dp"
 | 
				
			||||||
 | 
					                android:visibility="visible"
 | 
				
			||||||
                app:cardCornerRadius="12dp"
 | 
					                app:cardCornerRadius="12dp"
 | 
				
			||||||
                app:cardElevation="2dp"
 | 
					                app:cardElevation="2dp"
 | 
				
			||||||
                app:cardBackgroundColor="#F3F4F3">
 | 
					                app:cardBackgroundColor="#F3F4F3">
 | 
				
			||||||
@ -316,7 +384,6 @@
 | 
				
			|||||||
                </LinearLayout>
 | 
					                </LinearLayout>
 | 
				
			||||||
            </androidx.cardview.widget.CardView>
 | 
					            </androidx.cardview.widget.CardView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Row 3: Histori, Bantuan, Info Toko -->
 | 
					 | 
				
			||||||
            <androidx.cardview.widget.CardView
 | 
					            <androidx.cardview.widget.CardView
 | 
				
			||||||
                android:id="@+id/card_histori"
 | 
					                android:id="@+id/card_histori"
 | 
				
			||||||
                android:layout_width="0dp"
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
@ -324,6 +391,7 @@
 | 
				
			|||||||
                android:layout_columnWeight="1"
 | 
					                android:layout_columnWeight="1"
 | 
				
			||||||
                android:layout_rowWeight="1"
 | 
					                android:layout_rowWeight="1"
 | 
				
			||||||
                android:layout_margin="8dp"
 | 
					                android:layout_margin="8dp"
 | 
				
			||||||
 | 
					                android:visibility="visible"
 | 
				
			||||||
                app:cardCornerRadius="12dp"
 | 
					                app:cardCornerRadius="12dp"
 | 
				
			||||||
                app:cardElevation="2dp"
 | 
					                app:cardElevation="2dp"
 | 
				
			||||||
                app:cardBackgroundColor="#F3F4F3">
 | 
					                app:cardBackgroundColor="#F3F4F3">
 | 
				
			||||||
@ -350,6 +418,7 @@
 | 
				
			|||||||
                </LinearLayout>
 | 
					                </LinearLayout>
 | 
				
			||||||
            </androidx.cardview.widget.CardView>
 | 
					            </androidx.cardview.widget.CardView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Row 4: Bantuan, Info Toko, Pengaturan -->
 | 
				
			||||||
            <androidx.cardview.widget.CardView
 | 
					            <androidx.cardview.widget.CardView
 | 
				
			||||||
                android:id="@+id/card_bantuan"
 | 
					                android:id="@+id/card_bantuan"
 | 
				
			||||||
                android:layout_width="0dp"
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
@ -357,7 +426,7 @@
 | 
				
			|||||||
                android:layout_columnWeight="1"
 | 
					                android:layout_columnWeight="1"
 | 
				
			||||||
                android:layout_rowWeight="1"
 | 
					                android:layout_rowWeight="1"
 | 
				
			||||||
                android:layout_margin="8dp"
 | 
					                android:layout_margin="8dp"
 | 
				
			||||||
                android:visibility="visible"
 | 
					                android:visibility="gone"
 | 
				
			||||||
                app:cardCornerRadius="12dp"
 | 
					                app:cardCornerRadius="12dp"
 | 
				
			||||||
                app:cardElevation="2dp"
 | 
					                app:cardElevation="2dp"
 | 
				
			||||||
                app:cardBackgroundColor="#F3F4F3">
 | 
					                app:cardBackgroundColor="#F3F4F3">
 | 
				
			||||||
@ -391,7 +460,7 @@
 | 
				
			|||||||
                android:layout_columnWeight="1"
 | 
					                android:layout_columnWeight="1"
 | 
				
			||||||
                android:layout_rowWeight="1"
 | 
					                android:layout_rowWeight="1"
 | 
				
			||||||
                android:layout_margin="8dp"
 | 
					                android:layout_margin="8dp"
 | 
				
			||||||
                android:visibility="visible"
 | 
					                android:visibility="gone"
 | 
				
			||||||
                app:cardCornerRadius="12dp"
 | 
					                app:cardCornerRadius="12dp"
 | 
				
			||||||
                app:cardElevation="2dp"
 | 
					                app:cardElevation="2dp"
 | 
				
			||||||
                app:cardBackgroundColor="#F3F4F3">
 | 
					                app:cardBackgroundColor="#F3F4F3">
 | 
				
			||||||
@ -418,9 +487,8 @@
 | 
				
			|||||||
                </LinearLayout>
 | 
					                </LinearLayout>
 | 
				
			||||||
            </androidx.cardview.widget.CardView>
 | 
					            </androidx.cardview.widget.CardView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Row 4: Dummy Menu 1, 2, 3 (Hidden initially) -->
 | 
					 | 
				
			||||||
            <androidx.cardview.widget.CardView
 | 
					            <androidx.cardview.widget.CardView
 | 
				
			||||||
                android:id="@+id/card_dummy_menu_1"
 | 
					                android:id="@+id/card_pengaturan"
 | 
				
			||||||
                android:layout_width="0dp"
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
                android:layout_columnWeight="1"
 | 
					                android:layout_columnWeight="1"
 | 
				
			||||||
@ -441,189 +509,17 @@
 | 
				
			|||||||
                    <ImageView
 | 
					                    <ImageView
 | 
				
			||||||
                        android:layout_width="48dp"
 | 
					                        android:layout_width="48dp"
 | 
				
			||||||
                        android:layout_height="48dp"
 | 
					                        android:layout_height="48dp"
 | 
				
			||||||
                        android:src="@drawable/ic_qr_code"
 | 
					                        android:src="@drawable/ic_settings"
 | 
				
			||||||
                        app:tint="#E31937"/>
 | 
					                        app:tint="#E31937"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <TextView
 | 
					                    <TextView
 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
                        android:layout_marginTop="8dp"
 | 
					                        android:layout_marginTop="8dp"
 | 
				
			||||||
                        android:text="Dummy Menu 1"
 | 
					                        android:text="Pengaturan"
 | 
				
			||||||
                        style="@style/MenuCardTitle"/>
 | 
					                        style="@style/MenuCardTitle"/>
 | 
				
			||||||
                </LinearLayout>
 | 
					                </LinearLayout>
 | 
				
			||||||
            </androidx.cardview.widget.CardView>
 | 
					            </androidx.cardview.widget.CardView>
 | 
				
			||||||
 | 
					 | 
				
			||||||
            <androidx.cardview.widget.CardView
 | 
					 | 
				
			||||||
                android:id="@+id/card_dummy_menu_2"
 | 
					 | 
				
			||||||
                android:layout_width="0dp"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_columnWeight="1"
 | 
					 | 
				
			||||||
                android:layout_rowWeight="1"
 | 
					 | 
				
			||||||
                android:layout_margin="8dp"
 | 
					 | 
				
			||||||
                android:visibility="gone"
 | 
					 | 
				
			||||||
                app:cardCornerRadius="12dp"
 | 
					 | 
				
			||||||
                app:cardElevation="2dp"
 | 
					 | 
				
			||||||
                app:cardBackgroundColor="#F3F4F3">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="match_parent"
 | 
					 | 
				
			||||||
                    android:orientation="vertical"
 | 
					 | 
				
			||||||
                    android:gravity="center"
 | 
					 | 
				
			||||||
                    android:padding="16dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <ImageView
 | 
					 | 
				
			||||||
                        android:layout_width="48dp"
 | 
					 | 
				
			||||||
                        android:layout_height="48dp"
 | 
					 | 
				
			||||||
                        android:src="@drawable/ic_qr_code"
 | 
					 | 
				
			||||||
                        app:tint="#E31937"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                        android:text="Dummy Menu 2"
 | 
					 | 
				
			||||||
                        style="@style/MenuCardTitle"/>
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
            </androidx.cardview.widget.CardView>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <androidx.cardview.widget.CardView
 | 
					 | 
				
			||||||
                android:id="@+id/card_dummy_menu_3"
 | 
					 | 
				
			||||||
                android:layout_width="0dp"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_columnWeight="1"
 | 
					 | 
				
			||||||
                android:layout_rowWeight="1"
 | 
					 | 
				
			||||||
                android:layout_margin="8dp"
 | 
					 | 
				
			||||||
                android:visibility="gone"
 | 
					 | 
				
			||||||
                app:cardCornerRadius="12dp"
 | 
					 | 
				
			||||||
                app:cardElevation="2dp"
 | 
					 | 
				
			||||||
                app:cardBackgroundColor="#F3F4F3">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="match_parent"
 | 
					 | 
				
			||||||
                    android:orientation="vertical"
 | 
					 | 
				
			||||||
                    android:gravity="center"
 | 
					 | 
				
			||||||
                    android:padding="16dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <ImageView
 | 
					 | 
				
			||||||
                        android:layout_width="48dp"
 | 
					 | 
				
			||||||
                        android:layout_height="48dp"
 | 
					 | 
				
			||||||
                        android:src="@drawable/ic_qr_code"
 | 
					 | 
				
			||||||
                        app:tint="#E31937"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                        android:text="Dummy Menu 3"
 | 
					 | 
				
			||||||
                        style="@style/MenuCardTitle"/>
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
            </androidx.cardview.widget.CardView>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Row 5: Dummy Menu 4, 5, 6 (Hidden initially) -->
 | 
					 | 
				
			||||||
            <androidx.cardview.widget.CardView
 | 
					 | 
				
			||||||
                android:id="@+id/card_dummy_menu_4"
 | 
					 | 
				
			||||||
                android:layout_width="0dp"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_columnWeight="1"
 | 
					 | 
				
			||||||
                android:layout_rowWeight="1"
 | 
					 | 
				
			||||||
                android:layout_margin="8dp"
 | 
					 | 
				
			||||||
                android:visibility="gone"
 | 
					 | 
				
			||||||
                app:cardCornerRadius="12dp"
 | 
					 | 
				
			||||||
                app:cardElevation="2dp"
 | 
					 | 
				
			||||||
                app:cardBackgroundColor="#F3F4F3">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="match_parent"
 | 
					 | 
				
			||||||
                    android:orientation="vertical"
 | 
					 | 
				
			||||||
                    android:gravity="center"
 | 
					 | 
				
			||||||
                    android:padding="16dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <ImageView
 | 
					 | 
				
			||||||
                        android:layout_width="48dp"
 | 
					 | 
				
			||||||
                        android:layout_height="48dp"
 | 
					 | 
				
			||||||
                        android:src="@drawable/ic_qr_code"
 | 
					 | 
				
			||||||
                        app:tint="#E31937"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                        android:text="Dummy Menu 4"
 | 
					 | 
				
			||||||
                        style="@style/MenuCardTitle"/>
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
            </androidx.cardview.widget.CardView>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <androidx.cardview.widget.CardView
 | 
					 | 
				
			||||||
                android:id="@+id/card_dummy_menu_5"
 | 
					 | 
				
			||||||
                android:layout_width="0dp"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_columnWeight="1"
 | 
					 | 
				
			||||||
                android:layout_rowWeight="1"
 | 
					 | 
				
			||||||
                android:layout_margin="8dp"
 | 
					 | 
				
			||||||
                android:visibility="gone"
 | 
					 | 
				
			||||||
                app:cardCornerRadius="12dp"
 | 
					 | 
				
			||||||
                app:cardElevation="2dp"
 | 
					 | 
				
			||||||
                app:cardBackgroundColor="#F3F4F3">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="match_parent"
 | 
					 | 
				
			||||||
                    android:orientation="vertical"
 | 
					 | 
				
			||||||
                    android:gravity="center"
 | 
					 | 
				
			||||||
                    android:padding="16dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <ImageView
 | 
					 | 
				
			||||||
                        android:layout_width="48dp"
 | 
					 | 
				
			||||||
                        android:layout_height="48dp"
 | 
					 | 
				
			||||||
                        android:src="@drawable/ic_qr_code"
 | 
					 | 
				
			||||||
                        app:tint="#E31937"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                        android:text="Dummy Menu 5"
 | 
					 | 
				
			||||||
                        style="@style/MenuCardTitle"/>
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
            </androidx.cardview.widget.CardView>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <androidx.cardview.widget.CardView
 | 
					 | 
				
			||||||
                android:id="@+id/card_dummy_menu_6"
 | 
					 | 
				
			||||||
                android:layout_width="0dp"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_columnWeight="1"
 | 
					 | 
				
			||||||
                android:layout_rowWeight="1"
 | 
					 | 
				
			||||||
                android:layout_margin="8dp"
 | 
					 | 
				
			||||||
                android:visibility="gone"
 | 
					 | 
				
			||||||
                app:cardCornerRadius="12dp"
 | 
					 | 
				
			||||||
                app:cardElevation="2dp"
 | 
					 | 
				
			||||||
                app:cardBackgroundColor="#F3F4F3">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="match_parent"
 | 
					 | 
				
			||||||
                    android:orientation="vertical"
 | 
					 | 
				
			||||||
                    android:gravity="center"
 | 
					 | 
				
			||||||
                    android:padding="16dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <ImageView
 | 
					 | 
				
			||||||
                        android:layout_width="48dp"
 | 
					 | 
				
			||||||
                        android:layout_height="48dp"
 | 
					 | 
				
			||||||
                        android:src="@drawable/ic_qr_code"
 | 
					 | 
				
			||||||
                        app:tint="#E31937"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_marginTop="8dp"
 | 
					 | 
				
			||||||
                        android:text="Dummy Menu 6"
 | 
					 | 
				
			||||||
                        style="@style/MenuCardTitle"/>
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
            </androidx.cardview.widget.CardView>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        </GridLayout>
 | 
					        </GridLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <!-- Lainnya Button -->
 | 
					        <!-- Lainnya Button -->
 | 
				
			||||||
 | 
				
			|||||||
@ -1,268 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					 | 
				
			||||||
<ScrollView
 | 
					 | 
				
			||||||
    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:overScrollMode="never"
 | 
					 | 
				
			||||||
    android:scrollbars="none"
 | 
					 | 
				
			||||||
    android:background="#FFFFFF">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <androidx.constraintlayout.widget.ConstraintLayout
 | 
					 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					 | 
				
			||||||
        android:layout_height="match_parent"
 | 
					 | 
				
			||||||
        android:background="#FFFFFF"
 | 
					 | 
				
			||||||
        tools:context=".PaymentActivity">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- Red Status Bar (Override purple) -->
 | 
					 | 
				
			||||||
        <View
 | 
					 | 
				
			||||||
            android:id="@+id/red_status_bar"
 | 
					 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
            android:layout_height="24dp"
 | 
					 | 
				
			||||||
            android:background="#E31937"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toTopOf="parent"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- Red Background Header (Extended height untuk back navigation) -->
 | 
					 | 
				
			||||||
        <View
 | 
					 | 
				
			||||||
            android:id="@+id/red_header_background"
 | 
					 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
            android:layout_height="160dp"
 | 
					 | 
				
			||||||
            android:background="#E31937"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toBottomOf="@id/red_status_bar"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- Back Navigation (Positioned closer to status bar/appbar) -->
 | 
					 | 
				
			||||||
        <LinearLayout
 | 
					 | 
				
			||||||
            android:id="@+id/back_navigation"
 | 
					 | 
				
			||||||
            android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
            android:orientation="horizontal"
 | 
					 | 
				
			||||||
            android:gravity="center_vertical"
 | 
					 | 
				
			||||||
            android:layout_marginStart="16dp"
 | 
					 | 
				
			||||||
            android:layout_marginBottom="5dp"
 | 
					 | 
				
			||||||
            android:background="?attr/selectableItemBackgroundBorderless"
 | 
					 | 
				
			||||||
            android:padding="8dp"
 | 
					 | 
				
			||||||
            app:layout_constraintStart_toStartOf="parent"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toBottomOf="@id/red_status_bar">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Back Arrow -->
 | 
					 | 
				
			||||||
            <ImageView
 | 
					 | 
				
			||||||
                android:id="@+id/backArrow"
 | 
					 | 
				
			||||||
                android:layout_width="16dp"
 | 
					 | 
				
			||||||
                android:layout_height="16dp"
 | 
					 | 
				
			||||||
                android:src="@drawable/ic_arrow_back"
 | 
					 | 
				
			||||||
                android:contentDescription="Back" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Title Text -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/toolbarTitle"
 | 
					 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_marginStart="8dp"
 | 
					 | 
				
			||||||
                android:text="Kembali"
 | 
					 | 
				
			||||||
                android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                android:textSize="12sp"
 | 
					 | 
				
			||||||
                android:fontFamily="@font/inter"
 | 
					 | 
				
			||||||
                android:textStyle="normal" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- Payment Card -->
 | 
					 | 
				
			||||||
        <androidx.cardview.widget.CardView
 | 
					 | 
				
			||||||
            android:id="@+id/paymentCard"
 | 
					 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
            android:layout_height="191dp"
 | 
					 | 
				
			||||||
            android:layout_margin="16dp"
 | 
					 | 
				
			||||||
            android:layout_marginTop="5dp"
 | 
					 | 
				
			||||||
            app:cardBackgroundColor="#3498DB"
 | 
					 | 
				
			||||||
            app:cardCornerRadius="12dp"
 | 
					 | 
				
			||||||
            app:cardElevation="8dp"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toBottomOf="@id/back_navigation">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <LinearLayout
 | 
					 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                android:layout_height="match_parent"
 | 
					 | 
				
			||||||
                android:orientation="vertical"
 | 
					 | 
				
			||||||
                android:padding="20dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- Title -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:text="TOTAL PEMBAYARAN"
 | 
					 | 
				
			||||||
                    android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                    android:textSize="18sp"
 | 
					 | 
				
			||||||
                    android:textStyle="bold"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/inter"
 | 
					 | 
				
			||||||
                    android:layout_marginBottom="24dp" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- Amount Input Section -->
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:orientation="horizontal"
 | 
					 | 
				
			||||||
                    android:layout_marginBottom="8dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:text="RP"
 | 
					 | 
				
			||||||
                        android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                        android:textSize="20sp"
 | 
					 | 
				
			||||||
                        android:textStyle="bold"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/inter"
 | 
					 | 
				
			||||||
                        android:layout_marginEnd="8dp"
 | 
					 | 
				
			||||||
                        android:layout_gravity="bottom" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <EditText
 | 
					 | 
				
			||||||
                        android:id="@+id/editTextAmount"
 | 
					 | 
				
			||||||
                        android:layout_width="0dp"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_weight="1"
 | 
					 | 
				
			||||||
                        android:background="@android:color/transparent"
 | 
					 | 
				
			||||||
                        android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                        android:textColorHint="#80FFFFFF"
 | 
					 | 
				
			||||||
                        android:textSize="20sp"
 | 
					 | 
				
			||||||
                        android:textStyle="bold"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/inter"
 | 
					 | 
				
			||||||
                        android:hint=""
 | 
					 | 
				
			||||||
                        android:inputType="none"
 | 
					 | 
				
			||||||
                        android:focusable="false"
 | 
					 | 
				
			||||||
                        android:clickable="false"
 | 
					 | 
				
			||||||
                        android:cursorVisible="false"
 | 
					 | 
				
			||||||
                        android:text=""
 | 
					 | 
				
			||||||
                        android:gravity="start"
 | 
					 | 
				
			||||||
                        android:paddingBottom="4dp" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- White Underline -->
 | 
					 | 
				
			||||||
                <View
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="2dp"
 | 
					 | 
				
			||||||
                    android:background="@android:color/white"
 | 
					 | 
				
			||||||
                    android:layout_marginBottom="16dp" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- Description -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:text="Pastikan kembali nominal pembayaran pelanggan Anda"
 | 
					 | 
				
			||||||
                    android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                    android:textSize="12sp"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/inter"
 | 
					 | 
				
			||||||
                    android:alpha="0.9" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        </androidx.cardview.widget.CardView>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- Numpad -->
 | 
					 | 
				
			||||||
        <GridLayout
 | 
					 | 
				
			||||||
            android:id="@+id/numpad_grid"
 | 
					 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
            android:columnCount="3"
 | 
					 | 
				
			||||||
            android:rowCount="4"
 | 
					 | 
				
			||||||
            android:layout_marginTop="24dp"
 | 
					 | 
				
			||||||
            android:layout_marginStart="16dp"
 | 
					 | 
				
			||||||
            android:layout_marginEnd="16dp"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toBottomOf="@id/paymentCard">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Row 1: 1, 2, 3 -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn1"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="1" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn2"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="2" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn3"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="3" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Row 2: 4, 5, 6 -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn4"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="4" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn5"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="5" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn6"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="6" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Row 3: 7, 8, 9 -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn7"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="7" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn8"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="8" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn9"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="9" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Row 4: 000, 0, Delete -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn000"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="000" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn0"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="0" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <ImageView
 | 
					 | 
				
			||||||
                android:id="@+id/btnDelete"
 | 
					 | 
				
			||||||
                android:layout_width="0dp"
 | 
					 | 
				
			||||||
                android:layout_height="60dp"
 | 
					 | 
				
			||||||
                android:layout_columnWeight="1"
 | 
					 | 
				
			||||||
                android:layout_margin="8dp"
 | 
					 | 
				
			||||||
                android:background="?attr/selectableItemBackgroundBorderless"
 | 
					 | 
				
			||||||
                android:src="@drawable/ic_backspace"
 | 
					 | 
				
			||||||
                android:scaleType="center"
 | 
					 | 
				
			||||||
                android:contentDescription="Delete" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        </GridLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- Confirmation Button (UPDATED: Menggunakan MaterialButton) -->
 | 
					 | 
				
			||||||
        <com.google.android.material.button.MaterialButton
 | 
					 | 
				
			||||||
            android:id="@+id/confirmButton"
 | 
					 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
            android:layout_height="48dp"
 | 
					 | 
				
			||||||
            android:text="Konfirmasi"
 | 
					 | 
				
			||||||
            android:textColor="#FFFFFF"
 | 
					 | 
				
			||||||
            android:textSize="16sp"
 | 
					 | 
				
			||||||
            android:textStyle="bold"
 | 
					 | 
				
			||||||
            android:fontFamily="@font/inter"
 | 
					 | 
				
			||||||
            android:layout_marginStart="16dp"
 | 
					 | 
				
			||||||
            android:layout_marginEnd="16dp"
 | 
					 | 
				
			||||||
            android:layout_marginTop="24dp"
 | 
					 | 
				
			||||||
            android:layout_marginBottom="24dp"
 | 
					 | 
				
			||||||
            android:enabled="false"
 | 
					 | 
				
			||||||
            app:backgroundTint="#DE0701"
 | 
					 | 
				
			||||||
            app:cornerRadius="8dp"
 | 
					 | 
				
			||||||
            app:rippleColor="#B3000000"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toBottomOf="@id/numpad_grid"
 | 
					 | 
				
			||||||
            app:layout_constraintBottom_toBottomOf="parent"
 | 
					 | 
				
			||||||
            app:layout_constraintVertical_bias="1" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    </androidx.constraintlayout.widget.ConstraintLayout>
 | 
					 | 
				
			||||||
</ScrollView>
 | 
					 | 
				
			||||||
@ -1,298 +0,0 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					 | 
				
			||||||
<ScrollView
 | 
					 | 
				
			||||||
    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:overScrollMode="never"
 | 
					 | 
				
			||||||
    android:scrollbars="none"
 | 
					 | 
				
			||||||
    android:background="#F5F5F5">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <androidx.constraintlayout.widget.ConstraintLayout
 | 
					 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					 | 
				
			||||||
        android:layout_height="match_parent"
 | 
					 | 
				
			||||||
        android:background="#FFFFFF"
 | 
					 | 
				
			||||||
        tools:context=".PinActivity">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- Red Status Bar (Override purple) -->
 | 
					 | 
				
			||||||
        <View
 | 
					 | 
				
			||||||
            android:id="@+id/red_status_bar"
 | 
					 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
            android:layout_height="24dp"
 | 
					 | 
				
			||||||
            android:background="#E31937"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toTopOf="parent"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- Red Background Header (Extended height untuk back navigation) -->
 | 
					 | 
				
			||||||
        <View
 | 
					 | 
				
			||||||
            android:id="@+id/red_header_background"
 | 
					 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
            android:layout_height="160dp"
 | 
					 | 
				
			||||||
            android:background="#E31937"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toBottomOf="@id/red_status_bar"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- Header with Back Navigation -->
 | 
					 | 
				
			||||||
        <LinearLayout
 | 
					 | 
				
			||||||
            android:id="@+id/back_navigation"
 | 
					 | 
				
			||||||
            android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
            android:orientation="horizontal"
 | 
					 | 
				
			||||||
            android:gravity="center_vertical"
 | 
					 | 
				
			||||||
            android:layout_marginStart="16dp"
 | 
					 | 
				
			||||||
            android:layout_marginBottom="5dp"
 | 
					 | 
				
			||||||
            android:background="?attr/selectableItemBackgroundBorderless"
 | 
					 | 
				
			||||||
            android:padding="8dp"
 | 
					 | 
				
			||||||
            app:layout_constraintStart_toStartOf="parent"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toBottomOf="@id/red_status_bar">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Back Arrow -->
 | 
					 | 
				
			||||||
            <ImageView
 | 
					 | 
				
			||||||
                android:id="@+id/backArrow"
 | 
					 | 
				
			||||||
                android:layout_width="16dp"
 | 
					 | 
				
			||||||
                android:layout_height="16dp"
 | 
					 | 
				
			||||||
                android:src="@drawable/ic_arrow_back"
 | 
					 | 
				
			||||||
                android:contentDescription="Back" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Title Text -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/toolbarTitle"
 | 
					 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_marginStart="8dp"
 | 
					 | 
				
			||||||
                android:text="Kembali"
 | 
					 | 
				
			||||||
                android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                android:textSize="12sp"
 | 
					 | 
				
			||||||
                android:fontFamily="@font/inter"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- PIN Card -->
 | 
					 | 
				
			||||||
        <androidx.cardview.widget.CardView
 | 
					 | 
				
			||||||
            android:id="@+id/pin_card"
 | 
					 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
            android:layout_height="191dp"
 | 
					 | 
				
			||||||
            android:layout_margin="16dp"
 | 
					 | 
				
			||||||
            android:layout_marginTop="5dp"
 | 
					 | 
				
			||||||
            app:cardBackgroundColor="#3498DB"
 | 
					 | 
				
			||||||
            app:cardCornerRadius="12dp"
 | 
					 | 
				
			||||||
            app:cardElevation="8dp"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toBottomOf="@id/back_navigation">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <LinearLayout
 | 
					 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                android:layout_height="match_parent"
 | 
					 | 
				
			||||||
                android:orientation="vertical"
 | 
					 | 
				
			||||||
                android:padding="20dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- Title Text -->
 | 
					 | 
				
			||||||
                <TextView
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:text="SILAKAN MASUKAN PIN"
 | 
					 | 
				
			||||||
                    android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                    android:textSize="18sp"
 | 
					 | 
				
			||||||
                    android:textStyle="bold"
 | 
					 | 
				
			||||||
                    android:fontFamily="@font/inter"
 | 
					 | 
				
			||||||
                    android:layout_marginBottom="24dp"
 | 
					 | 
				
			||||||
                    android:gravity="center"
 | 
					 | 
				
			||||||
                    android:textAlignment="center"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- PIN Input Display -->
 | 
					 | 
				
			||||||
                <LinearLayout
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                    android:orientation="horizontal"
 | 
					 | 
				
			||||||
                    android:layout_marginBottom="8dp"
 | 
					 | 
				
			||||||
                    android:gravity="center">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                        android:textSize="20sp"
 | 
					 | 
				
			||||||
                        android:textStyle="bold"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/inter"
 | 
					 | 
				
			||||||
                        android:layout_marginEnd="8dp"
 | 
					 | 
				
			||||||
                        android:layout_gravity="center_vertical" />                    
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <EditText
 | 
					 | 
				
			||||||
                        android:id="@+id/editTextPin"
 | 
					 | 
				
			||||||
                        android:layout_width="0dp"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_weight="1"
 | 
					 | 
				
			||||||
                        android:background="@android:color/transparent"
 | 
					 | 
				
			||||||
                        android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                        android:textColorHint="#80FFFFFF"
 | 
					 | 
				
			||||||
                        android:textSize="20sp"
 | 
					 | 
				
			||||||
                        android:textStyle="bold"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/inter"
 | 
					 | 
				
			||||||
                        android:hint=""
 | 
					 | 
				
			||||||
                        android:inputType="none"
 | 
					 | 
				
			||||||
                        android:focusable="false"
 | 
					 | 
				
			||||||
                        android:clickable="false"
 | 
					 | 
				
			||||||
                        android:cursorVisible="false"
 | 
					 | 
				
			||||||
                        android:text=""
 | 
					 | 
				
			||||||
                        android:gravity="center"
 | 
					 | 
				
			||||||
                        android:textAlignment="center"
 | 
					 | 
				
			||||||
                        android:paddingBottom="4dp" />
 | 
					 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- White Underline -->
 | 
					 | 
				
			||||||
                <View
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="2dp"
 | 
					 | 
				
			||||||
                    android:background="@android:color/white"
 | 
					 | 
				
			||||||
                    android:layout_marginBottom="16dp" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            </LinearLayout>
 | 
					 | 
				
			||||||
        </androidx.cardview.widget.CardView>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- Numpad Grid -->
 | 
					 | 
				
			||||||
        <GridLayout
 | 
					 | 
				
			||||||
            android:id="@+id/numpad_grid"
 | 
					 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
            android:columnCount="3"
 | 
					 | 
				
			||||||
            android:rowCount="4"
 | 
					 | 
				
			||||||
            android:layout_margin="16dp"
 | 
					 | 
				
			||||||
            android:layout_marginTop="32dp"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toBottomOf="@id/pin_card">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Row 1: 1, 2, 3 -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn1"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="1"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn2"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="2"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn3"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="3"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Row 2: 4, 5, 6 -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn4"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="4"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn5"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="5"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn6"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="6"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Row 3: 7, 8, 9 -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn7"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="7"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn8"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="8"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn9"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="9"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Row 4: 000, 0, Delete -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn000"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="000"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/btn0"
 | 
					 | 
				
			||||||
                style="@style/NumpadButton"
 | 
					 | 
				
			||||||
                android:text="0"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <LinearLayout
 | 
					 | 
				
			||||||
                android:layout_width="0dp"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_columnWeight="1"
 | 
					 | 
				
			||||||
                android:layout_margin="8dp"
 | 
					 | 
				
			||||||
                android:gravity="center">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <ImageView
 | 
					 | 
				
			||||||
                    android:id="@+id/btnDelete"
 | 
					 | 
				
			||||||
                    android:layout_width="48dp"
 | 
					 | 
				
			||||||
                    android:layout_height="48dp"
 | 
					 | 
				
			||||||
                    android:src="@drawable/ic_backspace"
 | 
					 | 
				
			||||||
                    android:background="?android:attr/selectableItemBackgroundBorderless"
 | 
					 | 
				
			||||||
                    android:padding="12dp"
 | 
					 | 
				
			||||||
                    app:tint="#666666"/>
 | 
					 | 
				
			||||||
            </LinearLayout>
 | 
					 | 
				
			||||||
        </GridLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- Confirmation Button -->
 | 
					 | 
				
			||||||
        <Button
 | 
					 | 
				
			||||||
            android:id="@+id/confirmButton"
 | 
					 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
            android:layout_height="48dp"
 | 
					 | 
				
			||||||
            android:layout_margin="16dp"
 | 
					 | 
				
			||||||
            android:layout_marginTop="32dp"
 | 
					 | 
				
			||||||
            android:text="Konfirmasi"
 | 
					 | 
				
			||||||
            android:textColor="#999999"
 | 
					 | 
				
			||||||
            android:textSize="16sp"
 | 
					 | 
				
			||||||
            android:textStyle="bold"
 | 
					 | 
				
			||||||
            android:background="@drawable/button_inactive_background"
 | 
					 | 
				
			||||||
            android:enabled="false"
 | 
					 | 
				
			||||||
            android:alpha="0.6"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toBottomOf="@id/numpad_grid"
 | 
					 | 
				
			||||||
            app:layout_constraintBottom_toBottomOf="parent"
 | 
					 | 
				
			||||||
            app:layout_constraintVertical_bias="0"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        <!-- Success Screen (Full Screen Overlay) - IMPROVED VERSION -->
 | 
					 | 
				
			||||||
        <LinearLayout
 | 
					 | 
				
			||||||
            android:id="@+id/success_screen"
 | 
					 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					 | 
				
			||||||
            android:layout_height="match_parent"
 | 
					 | 
				
			||||||
            android:orientation="vertical"
 | 
					 | 
				
			||||||
            android:gravity="center"
 | 
					 | 
				
			||||||
            android:background="#E31937"
 | 
					 | 
				
			||||||
            android:visibility="gone"
 | 
					 | 
				
			||||||
            app:layout_constraintTop_toTopOf="parent"
 | 
					 | 
				
			||||||
            app:layout_constraintBottom_toBottomOf="parent"
 | 
					 | 
				
			||||||
            app:layout_constraintStart_toStartOf="parent"
 | 
					 | 
				
			||||||
            app:layout_constraintEnd_toEndOf="parent">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Success Icon -->
 | 
					 | 
				
			||||||
            <ImageView
 | 
					 | 
				
			||||||
                android:id="@+id/success_icon"
 | 
					 | 
				
			||||||
                android:layout_width="120dp"
 | 
					 | 
				
			||||||
                android:layout_height="120dp"
 | 
					 | 
				
			||||||
                android:src="@drawable/ic_success_payment"
 | 
					 | 
				
			||||||
                android:layout_marginBottom="32dp"
 | 
					 | 
				
			||||||
                android:scaleType="centerInside"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- Success Message -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/success_message"
 | 
					 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:text="Pembayaran Berhasil"
 | 
					 | 
				
			||||||
                android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                android:textSize="24sp"
 | 
					 | 
				
			||||||
                android:textStyle="bold"
 | 
					 | 
				
			||||||
                android:fontFamily="@font/inter"
 | 
					 | 
				
			||||||
                android:gravity="center"
 | 
					 | 
				
			||||||
                android:letterSpacing="0.02"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        </LinearLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    </androidx.constraintlayout.widget.ConstraintLayout>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</ScrollView>
 | 
					 | 
				
			||||||
@ -79,15 +79,14 @@
 | 
				
			|||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
                android:text="Cetak Ulang Struk"
 | 
					                android:text="Cetak Ulang Struk"
 | 
				
			||||||
                android:textColor="#333333"
 | 
					                android:textColor="#333333"
 | 
				
			||||||
                android:textSize="20sp"
 | 
					                android:textSize="16sp"
 | 
				
			||||||
                android:textStyle="bold"
 | 
					                android:textStyle="bold"
 | 
				
			||||||
                android:fontFamily="sans-serif" />
 | 
					                android:fontFamily="inter-bold" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        </LinearLayout>
 | 
					        </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </com.google.android.material.appbar.AppBarLayout>
 | 
					    </com.google.android.material.appbar.AppBarLayout>
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    <!-- ✅ PERBAIKAN: Gunakan LinearLayout dengan weight distribution yang lebih baik -->
 | 
					 | 
				
			||||||
    <LinearLayout
 | 
					    <LinearLayout
 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
        android:layout_height="match_parent"
 | 
					        android:layout_height="match_parent"
 | 
				
			||||||
@ -222,7 +221,6 @@
 | 
				
			|||||||
            android:layout_gravity="center_horizontal"
 | 
					            android:layout_gravity="center_horizontal"
 | 
				
			||||||
            android:layout_marginTop="8dp" />
 | 
					            android:layout_marginTop="8dp" />
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        <!-- ✅ PERBAIKAN: RecyclerView dengan height yang tepat untuk mencegah pagination terpotong -->
 | 
					 | 
				
			||||||
        <androidx.recyclerview.widget.RecyclerView
 | 
					        <androidx.recyclerview.widget.RecyclerView
 | 
				
			||||||
            android:id="@+id/recyclerView"
 | 
					            android:id="@+id/recyclerView"
 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
@ -232,7 +230,6 @@
 | 
				
			|||||||
            android:clipToPadding="false"
 | 
					            android:clipToPadding="false"
 | 
				
			||||||
            android:paddingBottom="8dp" />
 | 
					            android:paddingBottom="8dp" />
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        <!-- ✅ PERBAIKAN: Pagination Controls dengan padding dan margin yang lebih baik -->
 | 
					 | 
				
			||||||
        <LinearLayout
 | 
					        <LinearLayout
 | 
				
			||||||
            android:id="@+id/paginationControls"
 | 
					            android:id="@+id/paginationControls"
 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
@ -101,16 +101,10 @@
 | 
				
			|||||||
            android:clickable="true"
 | 
					            android:clickable="true"
 | 
				
			||||||
            android:focusable="true">
 | 
					            android:focusable="true">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <TextView
 | 
					            <ImageView
 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					                android:layout_width="48dp"
 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="48dp"
 | 
				
			||||||
                android:text="Cetak Ulang"
 | 
					                android:src="@drawable/ic_print" />
 | 
				
			||||||
                android:textColor="#666666"
 | 
					 | 
				
			||||||
                android:textSize="12sp"
 | 
					 | 
				
			||||||
                android:drawableLeft="@android:drawable/ic_menu_edit"
 | 
					 | 
				
			||||||
                android:drawablePadding="4dp"
 | 
					 | 
				
			||||||
                android:gravity="center_vertical" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        </LinearLayout>
 | 
					        </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </LinearLayout>
 | 
					    </LinearLayout>
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user