Compare commits
	
		
			26 Commits
		
	
	
		
			729bdddad4
			...
			6f78b6df3f
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 6f78b6df3f | |||
| da312ec3ae | |||
| 53964211c2 | |||
| b66ef4bb00 | |||
| 7a2ddc3f15 | |||
| 8a73206a76 | |||
| f6650f99d0 | |||
| 8ac97437a2 | |||
| 2b57d35553 | |||
| f2c3de9f5f | |||
| f5d9e53118 | |||
| ece79942c1 | |||
| 0af0e836b1 | |||
| f403358554 | |||
| d43c4bad0c | |||
| 174a1461fd | |||
| f4e5e03077 | |||
| f48e3e64a4 | |||
| 2ea0792d28 | |||
| 9834d4b841 | |||
| 8add903edb | |||
| 124da43a1e | |||
| d7617186a6 | |||
| 93fc410e37 | |||
| 448dfd9835 | |||
| eac3179d8a | 
@ -6,6 +6,13 @@ android {
 | 
				
			|||||||
    namespace 'com.example.bdkipoc'
 | 
					    namespace 'com.example.bdkipoc'
 | 
				
			||||||
    compileSdk 35
 | 
					    compileSdk 35
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
 | 
					    // Tambahkan lint options
 | 
				
			||||||
 | 
					    lint {
 | 
				
			||||||
 | 
					        abortOnError false
 | 
				
			||||||
 | 
					        disable 'GoogleAppIndexingWarning'
 | 
				
			||||||
 | 
					        disable 'NonConstantResourceId'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    defaultConfig {
 | 
					    defaultConfig {
 | 
				
			||||||
        applicationId "com.example.bdkipoc"
 | 
					        applicationId "com.example.bdkipoc"
 | 
				
			||||||
        minSdk 21
 | 
					        minSdk 21
 | 
				
			||||||
@ -22,20 +29,32 @@ android {
 | 
				
			|||||||
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
 | 
					            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Keep Java 11 - lebih modern dari referensi
 | 
				
			||||||
    compileOptions {
 | 
					    compileOptions {
 | 
				
			||||||
        sourceCompatibility JavaVersion.VERSION_11
 | 
					        sourceCompatibility JavaVersion.VERSION_11
 | 
				
			||||||
        targetCompatibility JavaVersion.VERSION_11
 | 
					        targetCompatibility JavaVersion.VERSION_11
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Tambahkan sourceSets untuk native libs jika diperlukan
 | 
				
			||||||
 | 
					    sourceSets {
 | 
				
			||||||
 | 
					        main {
 | 
				
			||||||
 | 
					            jniLibs.srcDirs = ['libs']
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dependencies {
 | 
					dependencies {
 | 
				
			||||||
 | 
					    implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
 | 
				
			||||||
    implementation libs.appcompat
 | 
					    implementation libs.appcompat
 | 
				
			||||||
    implementation libs.material
 | 
					    implementation libs.material
 | 
				
			||||||
    implementation libs.activity
 | 
					    implementation libs.activity
 | 
				
			||||||
    implementation libs.constraintlayout
 | 
					    implementation libs.constraintlayout
 | 
				
			||||||
    implementation libs.cardview
 | 
					    implementation libs.cardview
 | 
				
			||||||
    implementation 'androidx.recyclerview:recyclerview:1.3.0'
 | 
					    implementation 'androidx.recyclerview:recyclerview:1.3.0'
 | 
				
			||||||
 | 
					    implementation 'com.sunmi:printerlibrary:1.0.15'
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Test dependencies
 | 
				
			||||||
    testImplementation libs.junit
 | 
					    testImplementation libs.junit
 | 
				
			||||||
    androidTestImplementation libs.ext.junit
 | 
					    androidTestImplementation libs.ext.junit
 | 
				
			||||||
    androidTestImplementation libs.espresso.core
 | 
					    androidTestImplementation libs.espresso.core
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										
											BIN
										
									
								
								app/libs/PayLib-release-2.0.17-sources.jar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/PayLib-release-2.0.17-sources.jar
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/PayLib-release-2.0.17.aar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/PayLib-release-2.0.17.aar
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libAE_100.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libAE_100.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libCPACE_100.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libCPACE_100.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libDPAS_100.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libDPAS_100.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libEFTPOS_001.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libEFTPOS_001.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libEMVL2Base.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libEMVL2Base.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libEMVL2Dirct.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libEMVL2Dirct.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libEMV_100.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libEMV_100.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libEntry.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libEntry.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libFLASH_001.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libFLASH_001.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libJCB_100.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libJCB_100.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libMIR_001.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libMIR_001.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libPAGO_001.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libPAGO_001.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libPURE_001.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libPURE_001.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libPaypass_100.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libPaypass_100.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libPaywave_100.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libPaywave_100.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libQPBOC_100.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libQPBOC_100.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libRupay_001.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libRupay_001.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libSamsungPay_001.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libSamsungPay_001.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libsunmiemvl2.so
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/armeabi-v7a/libsunmiemvl2.so
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								app/libs/sunmiemvl2split-1.0.1.jar
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								app/libs/sunmiemvl2split-1.0.1.jar
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@ -8,7 +8,23 @@
 | 
				
			|||||||
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 | 
					    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
 | 
				
			||||||
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 | 
					    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <uses-permission android:name="com.sunmi.perm.LED" />
 | 
				
			||||||
 | 
					    <uses-permission android:name="com.sunmi.perm.MSR" />
 | 
				
			||||||
 | 
					    <uses-permission android:name="com.sunmi.perm.ICC" />
 | 
				
			||||||
 | 
					    <uses-permission android:name="com.sunmi.perm.PINPAD" />
 | 
				
			||||||
 | 
					    <uses-permission android:name="com.sunmi.perm.SECURITY" />
 | 
				
			||||||
 | 
					    <uses-permission android:name="com.sunmi.perm.CONTACTLESS_CARD" />
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.WAKE_LOCK" />
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
 | 
				
			||||||
 | 
					    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <uses-permission 
 | 
				
			||||||
 | 
					        android:name="android.permission.QUERY_ALL_PACKAGES"
 | 
				
			||||||
 | 
					        tools:ignore="QueryAllPackagesPermission" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <application
 | 
					    <application
 | 
				
			||||||
 | 
					        android:name=".MyApplication"
 | 
				
			||||||
        android:allowBackup="true"
 | 
					        android:allowBackup="true"
 | 
				
			||||||
        android:dataExtractionRules="@xml/data_extraction_rules"
 | 
					        android:dataExtractionRules="@xml/data_extraction_rules"
 | 
				
			||||||
        android:fullBackupContent="@xml/backup_rules"
 | 
					        android:fullBackupContent="@xml/backup_rules"
 | 
				
			||||||
@ -29,32 +45,45 @@
 | 
				
			|||||||
            </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" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <activity
 | 
				
			||||||
 | 
					            android:name=".transaction.CreateTransactionActivity"
 | 
				
			||||||
 | 
					            android:exported="false" />                
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        <activity
 | 
				
			||||||
 | 
					            android:name=".transaction.ResultTransactionActivity"
 | 
				
			||||||
 | 
					            android:exported="false" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <activity android:name="com.sunmi.emv.l2.view.AppSelectActivity"/>
 | 
				
			||||||
    </application>
 | 
					    </application>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</manifest>
 | 
					</manifest>
 | 
				
			||||||
							
								
								
									
										27
									
								
								app/src/main/java/com/example/bdkipoc/CacheHelper.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								app/src/main/java/com/example/bdkipoc/CacheHelper.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.content.SharedPreferences;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class CacheHelper {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final String PREFERENCE_FILE_NAME = "sm_pay_demo_obj";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final String KEY_LANGUAGE = "key_language";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void saveCurrentLanguage(int language) {
 | 
				
			||||||
 | 
					        SharedPreferences sharedPreferences = MyApplication.app.getSharedPreferences(PREFERENCE_FILE_NAME, Context.MODE_PRIVATE);
 | 
				
			||||||
 | 
					        int value = sharedPreferences.getInt(KEY_LANGUAGE, Constant.LANGUAGE_AUTO);
 | 
				
			||||||
 | 
					        if (value == language) return;
 | 
				
			||||||
 | 
					        SharedPreferences.Editor editor = sharedPreferences.edit();
 | 
				
			||||||
 | 
					        editor.putInt(KEY_LANGUAGE, language);
 | 
				
			||||||
 | 
					        editor.apply();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static int getCurrentLanguage() {
 | 
				
			||||||
 | 
					        SharedPreferences sharedPreferences = MyApplication.app.getSharedPreferences(PREFERENCE_FILE_NAME, Context.MODE_PRIVATE);
 | 
				
			||||||
 | 
					        return sharedPreferences.getInt(KEY_LANGUAGE, Constant.LANGUAGE_AUTO);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										17
									
								
								app/src/main/java/com/example/bdkipoc/Constant.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/src/main/java/com/example/bdkipoc/Constant.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class Constant {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String TAG = "SDKTestDemo";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final int LANGUAGE_AUTO = 0;
 | 
				
			||||||
 | 
					    public static final int LANGUAGE_ZH_CN = 1;
 | 
				
			||||||
 | 
					    public static final int LANGUAGE_EN_US = 2;
 | 
				
			||||||
 | 
					    public static final int LANGUAGE_JA_JP = 3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final int SCAN_MODEL_NONE = 100;
 | 
				
			||||||
 | 
					    public static final int SCAN_MODEL_P2Lite = 101;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final String SCAN_MODEL_NONE_VALUE = "NONE";
 | 
				
			||||||
 | 
					    public static final String SCAN_MODEL_P2Lite_VALUE = "P2Lite";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -19,6 +19,13 @@ 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.R;
 | 
				
			||||||
 | 
					import com.example.bdkipoc.transaction.CreateTransactionActivity;
 | 
				
			||||||
 | 
					import com.example.bdkipoc.transaction.ResultTransactionActivity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class MainActivity extends AppCompatActivity {
 | 
					public class MainActivity extends AppCompatActivity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private boolean isExpanded = false; // False = showing only 9 main menus, True = showing all 15 menus
 | 
					    private boolean isExpanded = false; // False = showing only 9 main menus, True = showing all 15 menus
 | 
				
			||||||
@ -83,12 +90,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) {
 | 
				
			||||||
@ -135,21 +139,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
 | 
				
			||||||
@ -157,39 +157,37 @@ public class MainActivity extends AppCompatActivity {
 | 
				
			|||||||
            CardView cardView = findViewById(cardId);
 | 
					            CardView cardView = findViewById(cardId);
 | 
				
			||||||
            if (cardView != null) {
 | 
					            if (cardView != null) {
 | 
				
			||||||
                cardView.setOnClickListener(v -> {
 | 
					                cardView.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					                    // ✅ ENHANCED: Navigate with payment type information
 | 
				
			||||||
                    if (cardId == R.id.card_kartu_kredit) {
 | 
					                    if (cardId == R.id.card_kartu_kredit) {
 | 
				
			||||||
                        startActivity(new Intent(MainActivity.this, PaymentActivity.class));
 | 
					                        navigateToCreateTransaction("credit_card", cardId, "Kartu Kredit");
 | 
				
			||||||
                    } else if (cardId == R.id.card_kartu_debit) {
 | 
					                    } else if (cardId == R.id.card_kartu_debit) {
 | 
				
			||||||
                        startActivity(new Intent(MainActivity.this, PaymentActivity.class));
 | 
					                        navigateToCreateTransaction("debit_card", cardId, "Kartu Debit");
 | 
				
			||||||
                    } 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) {
 | 
				
			||||||
 | 
					                        navigateToCreateTransaction("transfer", cardId, "Transfer");
 | 
				
			||||||
                    } else if (cardId == R.id.card_uang_elektronik) {
 | 
					                    } else if (cardId == R.id.card_uang_elektronik) {
 | 
				
			||||||
                        startActivity(new Intent(MainActivity.this, PaymentActivity.class));
 | 
					                        navigateToCreateTransaction("e_money", cardId, "Uang Elektronik");
 | 
				
			||||||
                    } 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) {
 | 
				
			||||||
 | 
					                        navigateToCreateTransaction("refund", cardId, "Refund");
 | 
				
			||||||
                    } 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();
 | 
					                        navigateToCreateTransaction("credit_card", cardId, "Unknown");
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                });
 | 
					                });
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@ -197,12 +195,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
 | 
				
			||||||
@ -249,6 +244,125 @@ public class MainActivity extends AppCompatActivity {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW: Enhanced navigation method with payment type information
 | 
				
			||||||
 | 
					    private void navigateToCreateTransaction(String paymentType, int cardMenuId, String cardName) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            Intent intent = new Intent(MainActivity.this, CreateTransactionActivity.class);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ ENHANCED: Pass comprehensive payment information
 | 
				
			||||||
 | 
					            intent.putExtra("PAYMENT_TYPE", paymentType);
 | 
				
			||||||
 | 
					            intent.putExtra("CARD_MENU_ID", cardMenuId);
 | 
				
			||||||
 | 
					            intent.putExtra("CARD_NAME", cardName);
 | 
				
			||||||
 | 
					            intent.putExtra("CALLING_ACTIVITY", "MainActivity");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ DEBUG: Log navigation details
 | 
				
			||||||
 | 
					            android.util.Log.d("MainActivity", "=== NAVIGATING TO CREATE TRANSACTION ===");
 | 
				
			||||||
 | 
					            android.util.Log.d("MainActivity", "Payment Type: " + paymentType);
 | 
				
			||||||
 | 
					            android.util.Log.d("MainActivity", "Card Menu ID: " + cardMenuId);
 | 
				
			||||||
 | 
					            android.util.Log.d("MainActivity", "Card Name: " + cardName);
 | 
				
			||||||
 | 
					            android.util.Log.d("MainActivity", "========================================");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            startActivity(intent);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            android.util.Log.e("MainActivity", "Error navigating to CreateTransaction: " + e.getMessage(), e);
 | 
				
			||||||
 | 
					            Toast.makeText(this, "Error opening transaction: " + e.getMessage(), Toast.LENGTH_SHORT).show();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW: Helper method to get payment type from card ID (for backward compatibility)
 | 
				
			||||||
 | 
					    private String getPaymentTypeFromCardId(int cardId) {
 | 
				
			||||||
 | 
					        if (cardId == R.id.card_kartu_kredit) {
 | 
				
			||||||
 | 
					            return "credit_card";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_kartu_debit) {
 | 
				
			||||||
 | 
					            return "debit_card";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_qris) {
 | 
				
			||||||
 | 
					            return "qris";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_transfer) {
 | 
				
			||||||
 | 
					            return "transfer";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_uang_elektronik) {
 | 
				
			||||||
 | 
					            return "e_money";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_refund) {
 | 
				
			||||||
 | 
					            return "refund";
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            android.util.Log.w("MainActivity", "Unknown card ID: " + cardId + ", defaulting to credit_card");
 | 
				
			||||||
 | 
					            return "credit_card";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW: Helper method to get card name from card ID
 | 
				
			||||||
 | 
					    private String getCardNameFromCardId(int cardId) {
 | 
				
			||||||
 | 
					        if (cardId == R.id.card_kartu_kredit) {
 | 
				
			||||||
 | 
					            return "Kartu Kredit";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_kartu_debit) {
 | 
				
			||||||
 | 
					            return "Kartu Debit";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_qris) {
 | 
				
			||||||
 | 
					            return "QRIS";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_transfer) {
 | 
				
			||||||
 | 
					            return "Transfer";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_uang_elektronik) {
 | 
				
			||||||
 | 
					            return "Uang Elektronik";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_refund) {
 | 
				
			||||||
 | 
					            return "Refund";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_settlement) {
 | 
				
			||||||
 | 
					            return "Settlement";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_histori) {
 | 
				
			||||||
 | 
					            return "Histori";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_cetak_ulang) {
 | 
				
			||||||
 | 
					            return "Cetak Ulang";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_bantuan) {
 | 
				
			||||||
 | 
					            return "Bantuan";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_info_toko) {
 | 
				
			||||||
 | 
					            return "Info Toko";
 | 
				
			||||||
 | 
					        } else if (cardId == R.id.card_pengaturan) {
 | 
				
			||||||
 | 
					            return "Pengaturan";
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return "Unknown";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW: Method to validate payment type compatibility
 | 
				
			||||||
 | 
					    private boolean isPaymentTypeSupported(String paymentType) {
 | 
				
			||||||
 | 
					        String[] supportedTypes = {
 | 
				
			||||||
 | 
					            "credit_card", "debit_card", "e_money", "qris", 
 | 
				
			||||||
 | 
					            "transfer", "refund"
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for (String supportedType : supportedTypes) {
 | 
				
			||||||
 | 
					            if (supportedType.equals(paymentType)) {
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW: Method to show payment type selection dialog (for future use)
 | 
				
			||||||
 | 
					    private void showPaymentTypeDialog() {
 | 
				
			||||||
 | 
					        androidx.appcompat.app.AlertDialog.Builder builder = new androidx.appcompat.app.AlertDialog.Builder(this);
 | 
				
			||||||
 | 
					        builder.setTitle("Pilih Jenis Pembayaran");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        String[] paymentTypes = {
 | 
				
			||||||
 | 
					            "Kartu Kredit", "Kartu Debit", "Uang Elektronik", 
 | 
				
			||||||
 | 
					            "QRIS", "Transfer", "Refund"
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        String[] paymentTypeCodes = {
 | 
				
			||||||
 | 
					            "credit_card", "debit_card", "e_money", 
 | 
				
			||||||
 | 
					            "qris", "transfer", "refund"
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        builder.setItems(paymentTypes, (dialog, which) -> {
 | 
				
			||||||
 | 
					            String selectedType = paymentTypeCodes[which];
 | 
				
			||||||
 | 
					            String selectedName = paymentTypes[which];
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Use a generic card ID for dialog selection
 | 
				
			||||||
 | 
					            navigateToCreateTransaction(selectedType, -1, selectedName);
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        builder.setNegativeButton("Batal", null);
 | 
				
			||||||
 | 
					        builder.show();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void onNewIntent(Intent intent) {
 | 
					    protected void onNewIntent(Intent intent) {
 | 
				
			||||||
        super.onNewIntent(intent);
 | 
					        super.onNewIntent(intent);
 | 
				
			||||||
@ -263,5 +377,74 @@ public class MainActivity extends AppCompatActivity {
 | 
				
			|||||||
        // Clear any transaction completion flags to avoid repeated messages
 | 
					        // Clear any transaction completion flags to avoid repeated messages
 | 
				
			||||||
        getIntent().removeExtra("transaction_completed");
 | 
					        getIntent().removeExtra("transaction_completed");
 | 
				
			||||||
        getIntent().removeExtra("transaction_amount");
 | 
					        getIntent().removeExtra("transaction_amount");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ NEW: Log resume for debugging
 | 
				
			||||||
 | 
					        android.util.Log.d("MainActivity", "MainActivity resumed");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onPause() {
 | 
				
			||||||
 | 
					        super.onPause();
 | 
				
			||||||
 | 
					        android.util.Log.d("MainActivity", "MainActivity paused");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onDestroy() {
 | 
				
			||||||
 | 
					        super.onDestroy();
 | 
				
			||||||
 | 
					        android.util.Log.d("MainActivity", "MainActivity destroyed");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW: Method to handle direct payment type launch (for external calls)
 | 
				
			||||||
 | 
					    public static Intent createTransactionIntent(android.content.Context context, String paymentType, String cardName) {
 | 
				
			||||||
 | 
					        Intent intent = new Intent(context, CreateTransactionActivity.class);
 | 
				
			||||||
 | 
					        intent.putExtra("PAYMENT_TYPE", paymentType);
 | 
				
			||||||
 | 
					        intent.putExtra("CARD_NAME", cardName);
 | 
				
			||||||
 | 
					        intent.putExtra("CALLING_ACTIVITY", "External");
 | 
				
			||||||
 | 
					        return intent;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW: Public method to simulate card click (for testing)
 | 
				
			||||||
 | 
					    public void simulateCardClick(int cardId) {
 | 
				
			||||||
 | 
					        CardView cardView = findViewById(cardId);
 | 
				
			||||||
 | 
					        if (cardView != null) {
 | 
				
			||||||
 | 
					            cardView.performClick();
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            android.util.Log.w("MainActivity", "Card not found for ID: " + cardId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW: Method to get all available payment types
 | 
				
			||||||
 | 
					    public String[] getAvailablePaymentTypes() {
 | 
				
			||||||
 | 
					        return new String[]{
 | 
				
			||||||
 | 
					            "credit_card", "debit_card", "e_money", 
 | 
				
			||||||
 | 
					            "qris", "transfer", "refund"
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW: Method to get payment type display names
 | 
				
			||||||
 | 
					    public String[] getPaymentTypeDisplayNames() {
 | 
				
			||||||
 | 
					        return new String[]{
 | 
				
			||||||
 | 
					            "Kartu Kredit", "Kartu Debit", "Uang Elektronik", 
 | 
				
			||||||
 | 
					            "QRIS", "Transfer", "Refund"
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ NEW: Debug method to log all card IDs and their payment types
 | 
				
			||||||
 | 
					    private void debugCardMappings() {
 | 
				
			||||||
 | 
					        android.util.Log.d("MainActivity", "=== CARD PAYMENT TYPE MAPPINGS ===");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        int[] cardIds = {
 | 
				
			||||||
 | 
					            R.id.card_kartu_kredit, R.id.card_kartu_debit, R.id.card_qris,
 | 
				
			||||||
 | 
					            R.id.card_transfer, R.id.card_uang_elektronik, R.id.card_refund
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        for (int cardId : cardIds) {
 | 
				
			||||||
 | 
					            String paymentType = getPaymentTypeFromCardId(cardId);
 | 
				
			||||||
 | 
					            String cardName = getCardNameFromCardId(cardId);
 | 
				
			||||||
 | 
					            android.util.Log.d("MainActivity", 
 | 
				
			||||||
 | 
					                "Card ID: " + cardId + " -> Payment Type: " + paymentType + " -> Name: " + cardName);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        android.util.Log.d("MainActivity", "==================================");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
							
								
								
									
										197
									
								
								app/src/main/java/com/example/bdkipoc/MyApplication.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								app/src/main/java/com/example/bdkipoc/MyApplication.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,197 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.app.Application;
 | 
				
			||||||
 | 
					import android.app.Service;
 | 
				
			||||||
 | 
					import android.content.ComponentName;
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.content.ServiceConnection;
 | 
				
			||||||
 | 
					import android.content.res.Configuration;
 | 
				
			||||||
 | 
					import android.content.res.Resources;
 | 
				
			||||||
 | 
					import android.os.IBinder;
 | 
				
			||||||
 | 
					import android.util.DisplayMetrics;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.emv.EmvTTS;
 | 
				
			||||||
 | 
					import com.example.bdkipoc.utils.LogUtil;
 | 
				
			||||||
 | 
					import com.example.bdkipoc.utils.Utility;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.emv.EMVOptV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.etc.ETCOptV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadOptV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.print.PrinterOptV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.readcard.ReadCardOptV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.rfid.RFIDOptV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.security.BiometricManagerV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.security.DevCertManagerV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.security.NoLostKeyManagerV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.security.SecurityOptV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.system.BasicOptV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.tax.TaxOptV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.test.TestOptV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.wrapper.HCEManagerV2Wrapper;
 | 
				
			||||||
 | 
					import com.sunmi.peripheral.printer.InnerPrinterCallback;
 | 
				
			||||||
 | 
					import com.sunmi.peripheral.printer.InnerPrinterException;
 | 
				
			||||||
 | 
					import com.sunmi.peripheral.printer.InnerPrinterManager;
 | 
				
			||||||
 | 
					import com.sunmi.peripheral.printer.SunmiPrinterService;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Locale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import sunmi.paylib.SunmiPayKernel;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class MyApplication extends Application {
 | 
				
			||||||
 | 
					    public static MyApplication app;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public BasicOptV2 basicOptV2;                   // 获取基础操作模块
 | 
				
			||||||
 | 
					    public ReadCardOptV2 readCardOptV2;             // 获取读卡模块
 | 
				
			||||||
 | 
					    public PinPadOptV2 pinPadOptV2;                 // 获取PinPad操作模块
 | 
				
			||||||
 | 
					    public SecurityOptV2 securityOptV2;             // 获取安全操作模块
 | 
				
			||||||
 | 
					    public EMVOptV2 emvOptV2;                       // 获取EMV操作模块
 | 
				
			||||||
 | 
					    public TaxOptV2 taxOptV2;                       // 获取税控操作模块
 | 
				
			||||||
 | 
					    public ETCOptV2 etcOptV2;                       // 获取ETC操作模块
 | 
				
			||||||
 | 
					    public PrinterOptV2 printerOptV2;               // 获取打印操作模块
 | 
				
			||||||
 | 
					    public TestOptV2 testOptV2;                     // 获取测试操作模块
 | 
				
			||||||
 | 
					    public DevCertManagerV2 devCertManagerV2;       // 设备证书操作模块
 | 
				
			||||||
 | 
					    public NoLostKeyManagerV2 noLostKeyManagerV2;   // NoLostKey操作模块
 | 
				
			||||||
 | 
					    public HCEManagerV2Wrapper hceV2Wrapper;        // HCE操作模块
 | 
				
			||||||
 | 
					    public RFIDOptV2 rfidOptV2;                     // RFID操作模块
 | 
				
			||||||
 | 
					    public SunmiPrinterService sunmiPrinterService; // 打印模块
 | 
				
			||||||
 | 
					    //public IScanInterface scanInterface;            // 扫码模块 (commented out)
 | 
				
			||||||
 | 
					    public BiometricManagerV2 mBiometricManagerV2;  // 生物特征模块
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private boolean connectPaySDK;//是否已连接PaySDK
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onCreate() {
 | 
				
			||||||
 | 
					        super.onCreate();
 | 
				
			||||||
 | 
					        app = this;
 | 
				
			||||||
 | 
					        initLocaleLanguage();
 | 
				
			||||||
 | 
					        initEmvTTS();
 | 
				
			||||||
 | 
					        bindPrintService();
 | 
				
			||||||
 | 
					        bindPaySDKService();
 | 
				
			||||||
 | 
					        //bindScannerService(); // Commented out scanner service binding
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void initLocaleLanguage() {
 | 
				
			||||||
 | 
					        Resources resources = app.getResources();
 | 
				
			||||||
 | 
					        DisplayMetrics dm = resources.getDisplayMetrics();
 | 
				
			||||||
 | 
					        Configuration config = resources.getConfiguration();
 | 
				
			||||||
 | 
					        int showLanguage = CacheHelper.getCurrentLanguage();
 | 
				
			||||||
 | 
					        if (showLanguage == Constant.LANGUAGE_AUTO) {
 | 
				
			||||||
 | 
					            LogUtil.e(Constant.TAG, config.locale.getCountry() + "---这是系统语言");
 | 
				
			||||||
 | 
					            config.locale = Resources.getSystem().getConfiguration().locale;
 | 
				
			||||||
 | 
					        } else if (showLanguage == Constant.LANGUAGE_ZH_CN) {
 | 
				
			||||||
 | 
					            LogUtil.e(Constant.TAG, "这是中文");
 | 
				
			||||||
 | 
					            config.locale = Locale.SIMPLIFIED_CHINESE;
 | 
				
			||||||
 | 
					        } else if (showLanguage == Constant.LANGUAGE_EN_US) {
 | 
				
			||||||
 | 
					            LogUtil.e(Constant.TAG, "这是英文");
 | 
				
			||||||
 | 
					            config.locale = Locale.ENGLISH;
 | 
				
			||||||
 | 
					        } else if (showLanguage == Constant.LANGUAGE_JA_JP) {
 | 
				
			||||||
 | 
					            LogUtil.e(Constant.TAG, "这是日文");
 | 
				
			||||||
 | 
					            config.locale = Locale.JAPAN;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        resources.updateConfiguration(config, dm);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onConfigurationChanged(Configuration newConfig) {
 | 
				
			||||||
 | 
					        super.onConfigurationChanged(newConfig);
 | 
				
			||||||
 | 
					        LogUtil.e(Constant.TAG, "onConfigurationChanged");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public boolean isConnectPaySDK() {
 | 
				
			||||||
 | 
					        return connectPaySDK;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * bind PaySDK service
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void bindPaySDKService() {
 | 
				
			||||||
 | 
					        final SunmiPayKernel payKernel = SunmiPayKernel.getInstance();
 | 
				
			||||||
 | 
					        payKernel.setEmvL2Split(true);
 | 
				
			||||||
 | 
					        payKernel.initPaySDK(this, new SunmiPayKernel.ConnectCallback() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void onConnectPaySDK() {
 | 
				
			||||||
 | 
					                LogUtil.e(Constant.TAG, "onConnectPaySDK...");
 | 
				
			||||||
 | 
					                emvOptV2 = payKernel.mEMVOptV2;
 | 
				
			||||||
 | 
					                basicOptV2 = payKernel.mBasicOptV2;
 | 
				
			||||||
 | 
					                pinPadOptV2 = payKernel.mPinPadOptV2;
 | 
				
			||||||
 | 
					                readCardOptV2 = payKernel.mReadCardOptV2;
 | 
				
			||||||
 | 
					                securityOptV2 = payKernel.mSecurityOptV2;
 | 
				
			||||||
 | 
					                taxOptV2 = payKernel.mTaxOptV2;
 | 
				
			||||||
 | 
					                etcOptV2 = payKernel.mETCOptV2;
 | 
				
			||||||
 | 
					                printerOptV2 = payKernel.mPrinterOptV2;
 | 
				
			||||||
 | 
					                testOptV2 = payKernel.mTestOptV2;
 | 
				
			||||||
 | 
					                devCertManagerV2 = payKernel.mDevCertManagerV2;
 | 
				
			||||||
 | 
					                noLostKeyManagerV2 = payKernel.mNoLostKeyManagerV2;
 | 
				
			||||||
 | 
					                mBiometricManagerV2 = payKernel.mBiometricManagerV2;
 | 
				
			||||||
 | 
					                hceV2Wrapper = payKernel.mHCEManagerV2Wrapper;
 | 
				
			||||||
 | 
					                rfidOptV2 = payKernel.mRFIDOptV2;
 | 
				
			||||||
 | 
					                connectPaySDK = true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void onDisconnectPaySDK() {
 | 
				
			||||||
 | 
					                LogUtil.e(Constant.TAG, "onDisconnectPaySDK...");
 | 
				
			||||||
 | 
					                connectPaySDK = false;
 | 
				
			||||||
 | 
					                emvOptV2 = null;
 | 
				
			||||||
 | 
					                basicOptV2 = null;
 | 
				
			||||||
 | 
					                pinPadOptV2 = null;
 | 
				
			||||||
 | 
					                readCardOptV2 = null;
 | 
				
			||||||
 | 
					                securityOptV2 = null;
 | 
				
			||||||
 | 
					                taxOptV2 = null;
 | 
				
			||||||
 | 
					                etcOptV2 = null;
 | 
				
			||||||
 | 
					                printerOptV2 = null;
 | 
				
			||||||
 | 
					                devCertManagerV2 = null;
 | 
				
			||||||
 | 
					                noLostKeyManagerV2 = null;
 | 
				
			||||||
 | 
					                mBiometricManagerV2 = null;
 | 
				
			||||||
 | 
					                rfidOptV2 = null;
 | 
				
			||||||
 | 
					                Utility.showToast(R.string.connect_fail);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * bind printer service
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void bindPrintService() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            InnerPrinterManager.getInstance().bindService(this, new InnerPrinterCallback() {
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                protected void onConnected(SunmiPrinterService service) {
 | 
				
			||||||
 | 
					                    sunmiPrinterService = service;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                protected void onDisconnected() {
 | 
				
			||||||
 | 
					                    sunmiPrinterService = null;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } catch (InnerPrinterException e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * bind scanner service (commented out)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					    public void bindScannerService() {
 | 
				
			||||||
 | 
					        Intent intent = new Intent();
 | 
				
			||||||
 | 
					        intent.setPackage("com.sunmi.scanner");
 | 
				
			||||||
 | 
					        intent.setAction("com.sunmi.scanner.IScanInterface");
 | 
				
			||||||
 | 
					        bindService(intent, new ServiceConnection() {
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void onServiceConnected(ComponentName name, IBinder service) {
 | 
				
			||||||
 | 
					                scanInterface = IScanInterface.Stub.asInterface(service);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            @Override
 | 
				
			||||||
 | 
					            public void onServiceDisconnected(ComponentName name) {
 | 
				
			||||||
 | 
					                scanInterface = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }, Service.BIND_AUTO_CREATE);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private void initEmvTTS() {
 | 
				
			||||||
 | 
					        EmvTTS.getInstance().init();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -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);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -1,689 +0,0 @@
 | 
				
			|||||||
package com.example.bdkipoc;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import android.content.Intent;
 | 
					 | 
				
			||||||
import android.graphics.Bitmap;
 | 
					 | 
				
			||||||
import android.graphics.BitmapFactory;
 | 
					 | 
				
			||||||
import android.os.AsyncTask;
 | 
					 | 
				
			||||||
import android.os.Bundle;
 | 
					 | 
				
			||||||
import android.os.Handler;
 | 
					 | 
				
			||||||
import android.os.Looper;
 | 
					 | 
				
			||||||
import android.util.Log;
 | 
					 | 
				
			||||||
import android.view.View;
 | 
					 | 
				
			||||||
import android.widget.*;
 | 
					 | 
				
			||||||
import androidx.annotation.Nullable;
 | 
					 | 
				
			||||||
import androidx.appcompat.app.AppCompatActivity;
 | 
					 | 
				
			||||||
import org.json.JSONArray;
 | 
					 | 
				
			||||||
import org.json.JSONObject;
 | 
					 | 
				
			||||||
import java.io.*;
 | 
					 | 
				
			||||||
import java.net.HttpURLConnection;
 | 
					 | 
				
			||||||
import java.net.URI;
 | 
					 | 
				
			||||||
import java.net.URL;
 | 
					 | 
				
			||||||
import java.text.NumberFormat;
 | 
					 | 
				
			||||||
import java.util.ArrayList;
 | 
					 | 
				
			||||||
import java.util.List;
 | 
					 | 
				
			||||||
import java.util.Locale;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public class QrisResultActivity extends AppCompatActivity {
 | 
					 | 
				
			||||||
    // UI Components
 | 
					 | 
				
			||||||
    private ImageView qrImageView;
 | 
					 | 
				
			||||||
    private TextView amountTextView, referenceTextView, statusTextView;
 | 
					 | 
				
			||||||
    private TextView timerTextView, qrStatusTextView;
 | 
					 | 
				
			||||||
    private Button downloadQrisButton, checkStatusButton, returnMainButton;
 | 
					 | 
				
			||||||
    private ProgressBar progressBar;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // QR Refresh Components
 | 
					 | 
				
			||||||
    private Handler qrRefreshHandler;
 | 
					 | 
				
			||||||
    private Runnable qrRefreshRunnable;
 | 
					 | 
				
			||||||
    private int countdownSeconds = 60;
 | 
					 | 
				
			||||||
    private boolean isQrRefreshActive = true;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Transaction Data
 | 
					 | 
				
			||||||
    private String orderId, grossAmount, referenceId, transactionId;
 | 
					 | 
				
			||||||
    private String transactionTime, acquirer, merchantId, currentQrImageUrl;
 | 
					 | 
				
			||||||
    private int originalAmount;
 | 
					 | 
				
			||||||
    private List<String> allOrderIds = new ArrayList<>();
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    // Configuration
 | 
					 | 
				
			||||||
    private static final String BACKEND_BASE = "https://be-edc.msvc.app";
 | 
					 | 
				
			||||||
    private static final String WEBHOOK_URL = "https://be-edc.msvc.app/webhooks/midtrans";
 | 
					 | 
				
			||||||
    private static final String MIDTRANS_AUTH = "Basic U0ItTWlkLXNlcnZlci1JM2RJWXdIRzVuamVMeHJCMVZ5endWMUM=";
 | 
					 | 
				
			||||||
    private static final String MIDTRANS_CHARGE_URL = "https://api.sandbox.midtrans.com/v2/charge";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void onCreate(@Nullable Bundle savedInstanceState) {
 | 
					 | 
				
			||||||
        super.onCreate(savedInstanceState);
 | 
					 | 
				
			||||||
        setContentView(R.layout.activity_qris_result);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        initializeViews();
 | 
					 | 
				
			||||||
        extractIntentData();
 | 
					 | 
				
			||||||
        validateAndSetupUI();
 | 
					 | 
				
			||||||
        startMonitoring();
 | 
					 | 
				
			||||||
        setupClickListeners();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void initializeViews() {
 | 
					 | 
				
			||||||
        qrImageView = findViewById(R.id.qrImageView);
 | 
					 | 
				
			||||||
        amountTextView = findViewById(R.id.amountTextView);
 | 
					 | 
				
			||||||
        referenceTextView = findViewById(R.id.referenceTextView);
 | 
					 | 
				
			||||||
        downloadQrisButton = findViewById(R.id.downloadQrisButton);
 | 
					 | 
				
			||||||
        checkStatusButton = findViewById(R.id.checkStatusButton);
 | 
					 | 
				
			||||||
        statusTextView = findViewById(R.id.statusTextView);
 | 
					 | 
				
			||||||
        returnMainButton = findViewById(R.id.returnMainButton);
 | 
					 | 
				
			||||||
        progressBar = findViewById(R.id.progressBar);
 | 
					 | 
				
			||||||
        timerTextView = findViewById(R.id.timerTextView);
 | 
					 | 
				
			||||||
        qrStatusTextView = findViewById(R.id.qrStatusTextView);
 | 
					 | 
				
			||||||
        qrRefreshHandler = new Handler(Looper.getMainLooper());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void extractIntentData() {
 | 
					 | 
				
			||||||
        Intent intent = getIntent();
 | 
					 | 
				
			||||||
        currentQrImageUrl = intent.getStringExtra("qrImageUrl");
 | 
					 | 
				
			||||||
        originalAmount = intent.getIntExtra("amount", 0);
 | 
					 | 
				
			||||||
        referenceId = intent.getStringExtra("referenceId");
 | 
					 | 
				
			||||||
        orderId = intent.getStringExtra("orderId");
 | 
					 | 
				
			||||||
        grossAmount = intent.getStringExtra("grossAmount");
 | 
					 | 
				
			||||||
        transactionId = intent.getStringExtra("transactionId");
 | 
					 | 
				
			||||||
        transactionTime = intent.getStringExtra("transactionTime");
 | 
					 | 
				
			||||||
        acquirer = intent.getStringExtra("acquirer");
 | 
					 | 
				
			||||||
        merchantId = intent.getStringExtra("merchantId");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        allOrderIds.add(orderId);
 | 
					 | 
				
			||||||
        Log.d("QrisResult", "Initialized with Order ID: " + orderId);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void validateAndSetupUI() {
 | 
					 | 
				
			||||||
        if (orderId == null || transactionId == null) {
 | 
					 | 
				
			||||||
            Toast.makeText(this, "Missing transaction details!", Toast.LENGTH_LONG).show();
 | 
					 | 
				
			||||||
            finish();
 | 
					 | 
				
			||||||
            return;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        String formattedAmount = formatCurrency(grossAmount != null ? grossAmount : String.valueOf(originalAmount));
 | 
					 | 
				
			||||||
        amountTextView.setText(formattedAmount);
 | 
					 | 
				
			||||||
        referenceTextView.setText("Reference ID: " + referenceId);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        loadQrImage(currentQrImageUrl);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        checkStatusButton.setEnabled(false);
 | 
					 | 
				
			||||||
        statusTextView.setText("Waiting for payment...");
 | 
					 | 
				
			||||||
        qrStatusTextView.setText("QR Code akan refresh dalam");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void startMonitoring() {
 | 
					 | 
				
			||||||
        startQrRefreshTimer();
 | 
					 | 
				
			||||||
        startPaymentMonitoring();
 | 
					 | 
				
			||||||
        pollPendingPaymentLog(orderId);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void startQrRefreshTimer() {
 | 
					 | 
				
			||||||
        countdownSeconds = 60;
 | 
					 | 
				
			||||||
        isQrRefreshActive = true;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        qrRefreshRunnable = new Runnable() {
 | 
					 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void run() {
 | 
					 | 
				
			||||||
                if (!isQrRefreshActive) return;
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                if (countdownSeconds > 0) {
 | 
					 | 
				
			||||||
                    timerTextView.setText(String.valueOf(countdownSeconds));
 | 
					 | 
				
			||||||
                    countdownSeconds--;
 | 
					 | 
				
			||||||
                    qrRefreshHandler.postDelayed(this, 1000);
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    refreshQrCode();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        qrRefreshHandler.post(qrRefreshRunnable);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void refreshQrCode() {
 | 
					 | 
				
			||||||
        if (!isQrRefreshActive) return;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        timerTextView.setText("...");
 | 
					 | 
				
			||||||
        qrStatusTextView.setText("Generating new QR Code...");
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        new Thread(() -> {
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                String newQrUrl = generateNewQrCode();
 | 
					 | 
				
			||||||
                runOnUiThread(() -> {
 | 
					 | 
				
			||||||
                    if (newQrUrl != null) {
 | 
					 | 
				
			||||||
                        currentQrImageUrl = newQrUrl;
 | 
					 | 
				
			||||||
                        loadQrImage(newQrUrl);
 | 
					 | 
				
			||||||
                        allOrderIds.add(orderId);
 | 
					 | 
				
			||||||
                        updateUIAfterRefresh();
 | 
					 | 
				
			||||||
                        countdownSeconds = 60;
 | 
					 | 
				
			||||||
                        qrStatusTextView.setText("QR Code akan refresh dalam");
 | 
					 | 
				
			||||||
                        Toast.makeText(this, "QR Code refreshed", Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        qrStatusTextView.setText("Failed to refresh QR - trying again in 30s");
 | 
					 | 
				
			||||||
                        countdownSeconds = 30;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                    qrRefreshHandler.postDelayed(qrRefreshRunnable, 1000);
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            } catch (Exception e) {
 | 
					 | 
				
			||||||
                Log.e("QrisResult", "QR refresh error: " + e.getMessage(), e);
 | 
					 | 
				
			||||||
                runOnUiThread(() -> {
 | 
					 | 
				
			||||||
                    qrStatusTextView.setText("QR refresh error - retrying in 30s");
 | 
					 | 
				
			||||||
                    countdownSeconds = 30;
 | 
					 | 
				
			||||||
                    qrRefreshHandler.postDelayed(qrRefreshRunnable, 1000);
 | 
					 | 
				
			||||||
                });
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }).start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private String generateNewQrCode() {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            String newOrderId = java.util.UUID.randomUUID().toString();
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            JSONObject customField = new JSONObject();
 | 
					 | 
				
			||||||
            customField.put("refresh_of", orderId);
 | 
					 | 
				
			||||||
            customField.put("refresh_time", getCurrentISOTime());
 | 
					 | 
				
			||||||
            customField.put("original_reference", referenceId);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            JSONObject payload = createQrisPayload(newOrderId, customField);
 | 
					 | 
				
			||||||
            String response = sendMidtransRequest(payload);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (response != null) {
 | 
					 | 
				
			||||||
                JSONObject jsonResponse = new JSONObject(response);
 | 
					 | 
				
			||||||
                if (jsonResponse.has("actions")) {
 | 
					 | 
				
			||||||
                    JSONArray actions = jsonResponse.getJSONArray("actions");
 | 
					 | 
				
			||||||
                    if (actions.length() > 0) {
 | 
					 | 
				
			||||||
                        String newQrUrl = actions.getJSONObject(0).getString("url");
 | 
					 | 
				
			||||||
                        
 | 
					 | 
				
			||||||
                        // Update transaction info
 | 
					 | 
				
			||||||
                        this.transactionId = jsonResponse.optString("transaction_id", transactionId);
 | 
					 | 
				
			||||||
                        this.transactionTime = jsonResponse.optString("transaction_time", transactionTime);
 | 
					 | 
				
			||||||
                        this.orderId = newOrderId;
 | 
					 | 
				
			||||||
                        
 | 
					 | 
				
			||||||
                        return newQrUrl;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (Exception e) {
 | 
					 | 
				
			||||||
            Log.e("QrisResult", "Generate QR error: " + e.getMessage(), e);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private JSONObject createQrisPayload(String orderIdParam, JSONObject customField) throws Exception {
 | 
					 | 
				
			||||||
        JSONObject payload = new JSONObject();
 | 
					 | 
				
			||||||
        payload.put("payment_type", "qris");
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        JSONObject transactionDetails = new JSONObject();
 | 
					 | 
				
			||||||
        transactionDetails.put("order_id", orderIdParam);
 | 
					 | 
				
			||||||
        transactionDetails.put("gross_amount", originalAmount);
 | 
					 | 
				
			||||||
        payload.put("transaction_details", transactionDetails);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        JSONObject customerDetails = new JSONObject();
 | 
					 | 
				
			||||||
        customerDetails.put("first_name", "Test");
 | 
					 | 
				
			||||||
        customerDetails.put("last_name", "Customer");
 | 
					 | 
				
			||||||
        customerDetails.put("email", "test@example.com");
 | 
					 | 
				
			||||||
        customerDetails.put("phone", "081234567890");
 | 
					 | 
				
			||||||
        payload.put("customer_details", customerDetails);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        JSONArray itemDetails = new JSONArray();
 | 
					 | 
				
			||||||
        JSONObject item = new JSONObject();
 | 
					 | 
				
			||||||
        item.put("id", "item1_refresh_" + System.currentTimeMillis());
 | 
					 | 
				
			||||||
        item.put("price", originalAmount);
 | 
					 | 
				
			||||||
        item.put("quantity", 1);
 | 
					 | 
				
			||||||
        item.put("name", "QRIS Payment - Refreshed (Ref: " + referenceId + ")");
 | 
					 | 
				
			||||||
        itemDetails.put(item);
 | 
					 | 
				
			||||||
        payload.put("item_details", itemDetails);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        payload.put("custom_field1", customField.toString());
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        return payload;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private String sendMidtransRequest(JSONObject payload) {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            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();
 | 
					 | 
				
			||||||
            if (responseCode == 200 || responseCode == 201) {
 | 
					 | 
				
			||||||
                BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
 | 
					 | 
				
			||||||
                StringBuilder response = new StringBuilder();
 | 
					 | 
				
			||||||
                String line;
 | 
					 | 
				
			||||||
                while ((line = br.readLine()) != null) {
 | 
					 | 
				
			||||||
                    response.append(line.trim());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                return response.toString();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (Exception e) {
 | 
					 | 
				
			||||||
            Log.e("QrisResult", "Midtrans request error: " + e.getMessage(), e);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return null;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void startPaymentMonitoring() {
 | 
					 | 
				
			||||||
        Handler paymentHandler = new Handler(Looper.getMainLooper());
 | 
					 | 
				
			||||||
        Runnable paymentRunnable = new Runnable() {
 | 
					 | 
				
			||||||
            @Override
 | 
					 | 
				
			||||||
            public void run() {
 | 
					 | 
				
			||||||
                checkAllOrderIdsStatus();
 | 
					 | 
				
			||||||
                if (!isFinishing() && isQrRefreshActive) {
 | 
					 | 
				
			||||||
                    paymentHandler.postDelayed(this, 3000);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
        paymentHandler.post(paymentRunnable);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void checkAllOrderIdsStatus() {
 | 
					 | 
				
			||||||
        new Thread(() -> {
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                for (String checkOrderId : allOrderIds) {
 | 
					 | 
				
			||||||
                    if (checkOrderId == null || checkOrderId.isEmpty()) continue;
 | 
					 | 
				
			||||||
                    
 | 
					 | 
				
			||||||
                    if (checkPaymentStatus(checkOrderId)) {
 | 
					 | 
				
			||||||
                        runOnUiThread(() -> {
 | 
					 | 
				
			||||||
                            stopQrRefresh();
 | 
					 | 
				
			||||||
                            syncTransactionStatusToBackend("PAID");
 | 
					 | 
				
			||||||
                            showPaymentSuccess();
 | 
					 | 
				
			||||||
                            Toast.makeText(this, "Payment Successful! 🎉", Toast.LENGTH_LONG).show();
 | 
					 | 
				
			||||||
                        });
 | 
					 | 
				
			||||||
                        return;
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } catch (Exception e) {
 | 
					 | 
				
			||||||
                Log.e("QrisResult", "Payment status check error: " + e.getMessage(), e);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }).start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private boolean checkPaymentStatus(String checkOrderId) {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            String urlStr = BACKEND_BASE + "/api-logs?request_body_search_strict=" + 
 | 
					 | 
				
			||||||
                java.net.URLEncoder.encode("{\"order_id\":\"" + checkOrderId + "\"}", "UTF-8");
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            URL url = new URL(urlStr);
 | 
					 | 
				
			||||||
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 | 
					 | 
				
			||||||
            conn.setRequestMethod("GET");
 | 
					 | 
				
			||||||
            conn.setRequestProperty("Accept", "application/json");
 | 
					 | 
				
			||||||
            conn.setConnectTimeout(5000);
 | 
					 | 
				
			||||||
            conn.setReadTimeout(5000);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            if (conn.getResponseCode() == 200) {
 | 
					 | 
				
			||||||
                BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));
 | 
					 | 
				
			||||||
                StringBuilder response = new StringBuilder();
 | 
					 | 
				
			||||||
                String line;
 | 
					 | 
				
			||||||
                while ((line = br.readLine()) != null) {
 | 
					 | 
				
			||||||
                    response.append(line);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                JSONObject json = new JSONObject(response.toString());
 | 
					 | 
				
			||||||
                JSONArray results = json.optJSONArray("results");
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                if (results != null && results.length() > 0) {
 | 
					 | 
				
			||||||
                    for (int i = 0; i < results.length(); i++) {
 | 
					 | 
				
			||||||
                        JSONObject log = results.getJSONObject(i);
 | 
					 | 
				
			||||||
                        JSONObject reqBody = log.optJSONObject("request_body");
 | 
					 | 
				
			||||||
                        
 | 
					 | 
				
			||||||
                        if (reqBody != null) {
 | 
					 | 
				
			||||||
                            String transactionStatus = reqBody.optString("transaction_status");
 | 
					 | 
				
			||||||
                            String logOrderId = reqBody.optString("order_id");
 | 
					 | 
				
			||||||
                            
 | 
					 | 
				
			||||||
                            if (checkOrderId.equals(logOrderId) && 
 | 
					 | 
				
			||||||
                                (transactionStatus.equals("settlement") || 
 | 
					 | 
				
			||||||
                                 transactionStatus.equals("capture") ||
 | 
					 | 
				
			||||||
                                 transactionStatus.equals("success"))) {
 | 
					 | 
				
			||||||
                                return true;
 | 
					 | 
				
			||||||
                            }
 | 
					 | 
				
			||||||
                        }
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (Exception e) {
 | 
					 | 
				
			||||||
            Log.e("QrisResult", "Payment check error: " + e.getMessage(), e);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        return false;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void loadQrImage(String qrImageUrl) {
 | 
					 | 
				
			||||||
        if (qrImageUrl != null && !qrImageUrl.isEmpty()) {
 | 
					 | 
				
			||||||
            new DownloadImageTask(qrImageView).execute(qrImageUrl);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            qrImageView.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
            downloadQrisButton.setEnabled(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void setupClickListeners() {
 | 
					 | 
				
			||||||
        downloadQrisButton.setOnClickListener(v -> downloadQrCode());
 | 
					 | 
				
			||||||
        checkStatusButton.setOnClickListener(v -> {
 | 
					 | 
				
			||||||
            stopQrRefresh();
 | 
					 | 
				
			||||||
            simulateWebhook();
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
        returnMainButton.setOnClickListener(v -> returnToMain());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void stopQrRefresh() {
 | 
					 | 
				
			||||||
        isQrRefreshActive = false;
 | 
					 | 
				
			||||||
        if (qrRefreshHandler != null && qrRefreshRunnable != null) {
 | 
					 | 
				
			||||||
            qrRefreshHandler.removeCallbacks(qrRefreshRunnable);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        timerTextView.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        qrStatusTextView.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void showPaymentSuccess() {
 | 
					 | 
				
			||||||
        stopQrRefresh();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        qrImageView.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        amountTextView.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        referenceTextView.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        downloadQrisButton.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        checkStatusButton.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        statusTextView.setText("✅ Payment Successful!\n\nTransaction ID: " + transactionId + 
 | 
					 | 
				
			||||||
                            "\nReference: " + referenceId + 
 | 
					 | 
				
			||||||
                            "\nAmount: " + formatCurrency(grossAmount));
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        returnMainButton.setVisibility(View.VISIBLE);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        new Handler(Looper.getMainLooper()).postDelayed(this::launchReceiptActivity, 2000);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void syncTransactionStatusToBackend(String status) {
 | 
					 | 
				
			||||||
        new Thread(() -> {
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                JSONObject updatePayload = new JSONObject();
 | 
					 | 
				
			||||||
                updatePayload.put("status", status);
 | 
					 | 
				
			||||||
                updatePayload.put("transaction_status", status);
 | 
					 | 
				
			||||||
                updatePayload.put("updated_at", getCurrentISOTime());
 | 
					 | 
				
			||||||
                updatePayload.put("settlement_time", getCurrentISOTime());
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                JSONObject body = new JSONObject();
 | 
					 | 
				
			||||||
                body.put("reference_id", referenceId);
 | 
					 | 
				
			||||||
                body.put("update_data", updatePayload);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                String updateUrl = BACKEND_BASE + "/transactions/update-by-reference";
 | 
					 | 
				
			||||||
                URL url = new URI(updateUrl).toURL();
 | 
					 | 
				
			||||||
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 | 
					 | 
				
			||||||
                conn.setRequestMethod("POST");
 | 
					 | 
				
			||||||
                conn.setRequestProperty("Content-Type", "application/json");
 | 
					 | 
				
			||||||
                conn.setDoOutput(true);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                try (OutputStream os = conn.getOutputStream()) {
 | 
					 | 
				
			||||||
                    byte[] input = body.toString().getBytes("utf-8");
 | 
					 | 
				
			||||||
                    os.write(input, 0, input.length);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                int responseCode = conn.getResponseCode();
 | 
					 | 
				
			||||||
                Log.d("QrisResult", "Backend sync response: " + responseCode);
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
            } catch (Exception e) {
 | 
					 | 
				
			||||||
                Log.e("QrisResult", "Backend sync error: " + e.getMessage(), e);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }).start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void simulateWebhook() {
 | 
					 | 
				
			||||||
        progressBar.setVisibility(View.VISIBLE);
 | 
					 | 
				
			||||||
        statusTextView.setText("Simulating payment...");
 | 
					 | 
				
			||||||
        checkStatusButton.setEnabled(false);
 | 
					 | 
				
			||||||
        stopQrRefresh();
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        new Thread(() -> {
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                JSONObject payload = createWebhookPayload();
 | 
					 | 
				
			||||||
                sendWebhookRequest(payload);
 | 
					 | 
				
			||||||
                Thread.sleep(2000);
 | 
					 | 
				
			||||||
            } catch (Exception e) {
 | 
					 | 
				
			||||||
                Log.e("QrisResult", "Webhook simulation error: " + e.getMessage(), e);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            runOnUiThread(() -> {
 | 
					 | 
				
			||||||
                progressBar.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
                showPaymentSuccess();
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }).start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private JSONObject createWebhookPayload() throws Exception {
 | 
					 | 
				
			||||||
        String serverKey = getServerKey();
 | 
					 | 
				
			||||||
        String signatureKey = generateSignature(orderId, "200", grossAmount, serverKey);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        JSONObject payload = new JSONObject();
 | 
					 | 
				
			||||||
        payload.put("transaction_type", "on-us");
 | 
					 | 
				
			||||||
        payload.put("transaction_time", transactionTime != null ? transactionTime : getCurrentISOTime());
 | 
					 | 
				
			||||||
        payload.put("transaction_status", "settlement");
 | 
					 | 
				
			||||||
        payload.put("transaction_id", transactionId);
 | 
					 | 
				
			||||||
        payload.put("status_message", "midtrans payment notification");
 | 
					 | 
				
			||||||
        payload.put("status_code", "200");
 | 
					 | 
				
			||||||
        payload.put("signature_key", signatureKey);
 | 
					 | 
				
			||||||
        payload.put("payment_type", "qris");
 | 
					 | 
				
			||||||
        payload.put("order_id", orderId);
 | 
					 | 
				
			||||||
        payload.put("gross_amount", grossAmount);
 | 
					 | 
				
			||||||
        payload.put("reference_id", referenceId);
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        return payload;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void sendWebhookRequest(JSONObject payload) {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            URL url = new URL(WEBHOOK_URL);
 | 
					 | 
				
			||||||
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 | 
					 | 
				
			||||||
            conn.setRequestMethod("POST");
 | 
					 | 
				
			||||||
            conn.setRequestProperty("Content-Type", "application/json");
 | 
					 | 
				
			||||||
            conn.setDoOutput(true);
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            try (OutputStream os = conn.getOutputStream()) {
 | 
					 | 
				
			||||||
                os.write(payload.toString().getBytes());
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            Log.d("QrisResult", "Webhook response: " + conn.getResponseCode());
 | 
					 | 
				
			||||||
        } catch (Exception e) {
 | 
					 | 
				
			||||||
            Log.e("QrisResult", "Webhook request error: " + e.getMessage(), e);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Utility Methods
 | 
					 | 
				
			||||||
    private String formatCurrency(String amount) {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            double amountDouble = Double.parseDouble(amount);
 | 
					 | 
				
			||||||
            NumberFormat formatter = NumberFormat.getCurrencyInstance(new Locale("id", "ID"));
 | 
					 | 
				
			||||||
            return formatter.format(amountDouble);
 | 
					 | 
				
			||||||
        } catch (NumberFormatException e) {
 | 
					 | 
				
			||||||
            return "IDR " + amount;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private String getCurrentISOTime() {
 | 
					 | 
				
			||||||
        return new java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
 | 
					 | 
				
			||||||
            .format(new java.util.Date());
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private String getServerKey() {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            String base64 = MIDTRANS_AUTH.replace("Basic ", "");
 | 
					 | 
				
			||||||
            byte[] decoded = android.util.Base64.decode(base64, android.util.Base64.DEFAULT);
 | 
					 | 
				
			||||||
            return new String(decoded).replace(":", "");
 | 
					 | 
				
			||||||
        } catch (Exception e) {
 | 
					 | 
				
			||||||
            return "";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private String generateSignature(String orderId, String statusCode, String grossAmount, String serverKey) {
 | 
					 | 
				
			||||||
        String input = orderId + statusCode + grossAmount + serverKey;
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-512");
 | 
					 | 
				
			||||||
            byte[] messageDigest = md.digest(input.getBytes());
 | 
					 | 
				
			||||||
            StringBuilder hexString = new StringBuilder();
 | 
					 | 
				
			||||||
            for (byte b : messageDigest) {
 | 
					 | 
				
			||||||
                String hex = Integer.toHexString(0xff & b);
 | 
					 | 
				
			||||||
                if (hex.length() == 1) hexString.append('0');
 | 
					 | 
				
			||||||
                hexString.append(hex);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return hexString.toString();
 | 
					 | 
				
			||||||
        } catch (Exception e) {
 | 
					 | 
				
			||||||
            return "dummy_signature";
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Navigation and Lifecycle
 | 
					 | 
				
			||||||
    private void returnToMain() {
 | 
					 | 
				
			||||||
        stopQrRefresh();
 | 
					 | 
				
			||||||
        Intent intent = new Intent(this, MainActivity.class);
 | 
					 | 
				
			||||||
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
					 | 
				
			||||||
        startActivity(intent);
 | 
					 | 
				
			||||||
        finishAffinity();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void launchReceiptActivity() {
 | 
					 | 
				
			||||||
        Intent intent = new Intent(this, ReceiptActivity.class);
 | 
					 | 
				
			||||||
        intent.putExtra("calling_activity", "QrisResultActivity");
 | 
					 | 
				
			||||||
        intent.putExtra("transaction_id", transactionId);
 | 
					 | 
				
			||||||
        intent.putExtra("reference_id", referenceId);
 | 
					 | 
				
			||||||
        intent.putExtra("order_id", orderId);
 | 
					 | 
				
			||||||
        intent.putExtra("transaction_amount", String.valueOf(originalAmount));
 | 
					 | 
				
			||||||
        intent.putExtra("gross_amount", grossAmount != null ? grossAmount : String.valueOf(originalAmount));
 | 
					 | 
				
			||||||
        intent.putExtra("created_at", getCurrentISOTime());
 | 
					 | 
				
			||||||
        intent.putExtra("payment_method", "QRIS");
 | 
					 | 
				
			||||||
        intent.putExtra("acquirer", acquirer != null ? acquirer : "qris");
 | 
					 | 
				
			||||||
        intent.putExtra("mid", "71000026521");
 | 
					 | 
				
			||||||
        intent.putExtra("tid", "73001500");
 | 
					 | 
				
			||||||
        startActivity(intent);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void downloadQrCode() {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            qrImageView.setDrawingCacheEnabled(true);
 | 
					 | 
				
			||||||
            qrImageView.buildDrawingCache();
 | 
					 | 
				
			||||||
            Bitmap bitmap = qrImageView.getDrawingCache();
 | 
					 | 
				
			||||||
            if (bitmap != null) {
 | 
					 | 
				
			||||||
                saveImageToGallery(bitmap, "qris_code_" + System.currentTimeMillis());
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                Toast.makeText(this, "Unable to capture QR code image", Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (Exception e) {
 | 
					 | 
				
			||||||
            Toast.makeText(this, "Error downloading QR code", Toast.LENGTH_LONG).show();
 | 
					 | 
				
			||||||
        } finally {
 | 
					 | 
				
			||||||
            qrImageView.setDrawingCacheEnabled(false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void saveImageToGallery(Bitmap bitmap, String fileName) {
 | 
					 | 
				
			||||||
        try {
 | 
					 | 
				
			||||||
            String savedImageURL = android.provider.MediaStore.Images.Media.insertImage(
 | 
					 | 
				
			||||||
                    getContentResolver(), bitmap, fileName, "QRIS Payment QR Code");
 | 
					 | 
				
			||||||
            if (savedImageURL != null) {
 | 
					 | 
				
			||||||
                Toast.makeText(this, "QRIS saved to gallery", Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                Toast.makeText(this, "Failed to save QRIS", Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        } catch (Exception e) {
 | 
					 | 
				
			||||||
            Toast.makeText(this, "Error saving QRIS", Toast.LENGTH_LONG).show();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void updateUIAfterRefresh() {
 | 
					 | 
				
			||||||
        String refreshTime = new java.text.SimpleDateFormat("HH:mm:ss").format(new java.util.Date());
 | 
					 | 
				
			||||||
        referenceTextView.setText("Reference ID: " + referenceId + " (Refreshed at " + refreshTime + ")");
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    private void pollPendingPaymentLog(String orderId) {
 | 
					 | 
				
			||||||
        progressBar.setVisibility(View.VISIBLE);
 | 
					 | 
				
			||||||
        statusTextView.setText("Checking payment status...");
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        new Thread(() -> {
 | 
					 | 
				
			||||||
            int maxAttempts = 12;
 | 
					 | 
				
			||||||
            boolean found = false;
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            for (int attempt = 0; attempt < maxAttempts && !found; attempt++) {
 | 
					 | 
				
			||||||
                try {
 | 
					 | 
				
			||||||
                    found = checkPaymentStatus(orderId);
 | 
					 | 
				
			||||||
                    if (!found && attempt < maxAttempts - 1) {
 | 
					 | 
				
			||||||
                        Thread.sleep(2000);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } catch (Exception e) {
 | 
					 | 
				
			||||||
                    Log.e("QrisResult", "Polling error: " + e.getMessage());
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            final boolean logFound = found;
 | 
					 | 
				
			||||||
            runOnUiThread(() -> {
 | 
					 | 
				
			||||||
                progressBar.setVisibility(View.GONE);
 | 
					 | 
				
			||||||
                if (logFound) {
 | 
					 | 
				
			||||||
                    checkStatusButton.setEnabled(true);
 | 
					 | 
				
			||||||
                    statusTextView.setText("Ready to simulate payment");
 | 
					 | 
				
			||||||
                    Toast.makeText(this, "Payment log found!", Toast.LENGTH_SHORT).show();
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    statusTextView.setText("Payment log not found");
 | 
					 | 
				
			||||||
                    checkStatusButton.setEnabled(true);
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            });
 | 
					 | 
				
			||||||
        }).start();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    protected void onDestroy() {
 | 
					 | 
				
			||||||
        super.onDestroy();
 | 
					 | 
				
			||||||
        stopQrRefresh();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @Override
 | 
					 | 
				
			||||||
    public void onBackPressed() {
 | 
					 | 
				
			||||||
        stopQrRefresh();
 | 
					 | 
				
			||||||
        returnToMain();
 | 
					 | 
				
			||||||
        super.onBackPressed();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // AsyncTask for downloading QR image
 | 
					 | 
				
			||||||
    private static class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
 | 
					 | 
				
			||||||
        private ImageView imageView;
 | 
					 | 
				
			||||||
        private String errorMessage;
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        DownloadImageTask(ImageView imageView) {
 | 
					 | 
				
			||||||
            this.imageView = imageView;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        protected Bitmap doInBackground(String... urls) {
 | 
					 | 
				
			||||||
            try {
 | 
					 | 
				
			||||||
                URL url = new URI(urls[0]).toURL();
 | 
					 | 
				
			||||||
                HttpURLConnection connection = (HttpURLConnection) url.openConnection();
 | 
					 | 
				
			||||||
                connection.setDoInput(true);
 | 
					 | 
				
			||||||
                connection.setConnectTimeout(10000);
 | 
					 | 
				
			||||||
                connection.setReadTimeout(10000);
 | 
					 | 
				
			||||||
                connection.connect();
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                if (connection.getResponseCode() == 200) {
 | 
					 | 
				
			||||||
                    InputStream input = connection.getInputStream();
 | 
					 | 
				
			||||||
                    return BitmapFactory.decodeStream(input);
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    errorMessage = "Failed to download QR code";
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } catch (Exception e) {
 | 
					 | 
				
			||||||
                errorMessage = "Error downloading QR code: " + e.getMessage();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            return null;
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        
 | 
					 | 
				
			||||||
        @Override
 | 
					 | 
				
			||||||
        protected void onPostExecute(Bitmap result) {
 | 
					 | 
				
			||||||
            if (result != null) {
 | 
					 | 
				
			||||||
                imageView.setImageBitmap(result);
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                imageView.setImageResource(android.R.drawable.ic_menu_report_image);
 | 
					 | 
				
			||||||
                if (errorMessage != null && imageView.getContext() != null) {
 | 
					 | 
				
			||||||
                    Toast.makeText(imageView.getContext(), errorMessage, Toast.LENGTH_LONG).show();
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@ -23,6 +23,10 @@ import java.net.HttpURLConnection;
 | 
				
			|||||||
import java.io.OutputStream;
 | 
					import java.io.OutputStream;
 | 
				
			||||||
import java.net.URL;
 | 
					import java.net.URL;
 | 
				
			||||||
import java.net.URI;
 | 
					import java.net.URI;
 | 
				
			||||||
 | 
					import java.util.HashMap;
 | 
				
			||||||
 | 
					import java.util.Map;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.cetakulang.ReprintActivity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public class ReceiptActivity extends AppCompatActivity {
 | 
					public class ReceiptActivity extends AppCompatActivity {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -50,6 +54,28 @@ public class ReceiptActivity extends AppCompatActivity {
 | 
				
			|||||||
    private LinearLayout emailButton;
 | 
					    private LinearLayout emailButton;
 | 
				
			||||||
    private Button finishButton;
 | 
					    private Button finishButton;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ✅ ENHANCED: Mapping dari technical issuer ke display name
 | 
				
			||||||
 | 
					    private static final Map<String, String> ISSUER_DISPLAY_MAP = new HashMap<String, String>() {{
 | 
				
			||||||
 | 
					        put("airpay shopee", "ShopeePay");
 | 
				
			||||||
 | 
					        put("shopeepay", "ShopeePay");
 | 
				
			||||||
 | 
					        put("shopee", "ShopeePay");
 | 
				
			||||||
 | 
					        put("linkaja", "LinkAja");
 | 
				
			||||||
 | 
					        put("link aja", "LinkAja");
 | 
				
			||||||
 | 
					        put("dana", "DANA");
 | 
				
			||||||
 | 
					        put("ovo", "OVO");
 | 
				
			||||||
 | 
					        put("gopay", "GoPay");
 | 
				
			||||||
 | 
					        put("jenius", "Jenius");
 | 
				
			||||||
 | 
					        put("sakuku", "Sakuku");
 | 
				
			||||||
 | 
					        put("bni", "BNI");
 | 
				
			||||||
 | 
					        put("bca", "BCA");
 | 
				
			||||||
 | 
					        put("mandiri", "Mandiri");
 | 
				
			||||||
 | 
					        put("bri", "BRI");
 | 
				
			||||||
 | 
					        put("cimb", "CIMB Niaga");
 | 
				
			||||||
 | 
					        put("permata", "Permata");
 | 
				
			||||||
 | 
					        put("maybank", "Maybank");
 | 
				
			||||||
 | 
					        put("qris", "QRIS");
 | 
				
			||||||
 | 
					    }};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
    protected void onCreate(Bundle savedInstanceState) {
 | 
					    protected void onCreate(Bundle savedInstanceState) {
 | 
				
			||||||
        super.onCreate(savedInstanceState);
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
@ -116,10 +142,27 @@ public class ReceiptActivity extends AppCompatActivity {
 | 
				
			|||||||
        finishButton.setOnClickListener(v -> handleFinish());
 | 
					        finishButton.setOnClickListener(v -> handleFinish());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * ✅ ENHANCED: Load transaction data with support for both EMV/Card and QRIS
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    private void loadTransactionData() {
 | 
					    private void loadTransactionData() {
 | 
				
			||||||
        Intent intent = getIntent();
 | 
					        Intent intent = getIntent();
 | 
				
			||||||
        if (intent != null) {
 | 
					        if (intent != null) {
 | 
				
			||||||
            Log.d("ReceiptActivity", "=== LOADING TRANSACTION DATA ===");
 | 
					            Log.d("ReceiptActivity", "=== LOADING ENHANCED TRANSACTION DATA ===");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ DETECT TRANSACTION TYPE
 | 
				
			||||||
 | 
					            String callingActivity = intent.getStringExtra("calling_activity");
 | 
				
			||||||
 | 
					            String channelCategory = intent.getStringExtra("channel_category");
 | 
				
			||||||
 | 
					            String channelCode = intent.getStringExtra("channel_code");
 | 
				
			||||||
 | 
					            boolean isEmvTransaction = "EMV_CARD".equals(channelCategory) || "ResultTransactionActivity".equals(callingActivity);
 | 
				
			||||||
 | 
					            boolean isQrisTransaction = "QRIS".equalsIgnoreCase(channelCode) || "QrisResultActivity".equals(callingActivity);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "🔍 TRANSACTION TYPE DETECTION:");
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "   Calling Activity: " + callingActivity);
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "   Channel Category: " + channelCategory);
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "   Channel Code: " + channelCode);
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "   Is EMV Transaction: " + isEmvTransaction);
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "   Is QRIS Transaction: " + isQrisTransaction);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Get all available data from intent
 | 
					            // Get all available data from intent
 | 
				
			||||||
            String amount = intent.getStringExtra("transaction_amount");
 | 
					            String amount = intent.getStringExtra("transaction_amount");
 | 
				
			||||||
@ -133,89 +176,144 @@ public class ReceiptActivity extends AppCompatActivity {
 | 
				
			|||||||
            String createdAt = intent.getStringExtra("created_at");
 | 
					            String createdAt = intent.getStringExtra("created_at");
 | 
				
			||||||
            String paymentMethodStr = intent.getStringExtra("payment_method");
 | 
					            String paymentMethodStr = intent.getStringExtra("payment_method");
 | 
				
			||||||
            String cardTypeStr = intent.getStringExtra("card_type");
 | 
					            String cardTypeStr = intent.getStringExtra("card_type");
 | 
				
			||||||
            String channelCode = intent.getStringExtra("channel_code");
 | 
					 | 
				
			||||||
            String channelCategory = intent.getStringExtra("channel_category");
 | 
					 | 
				
			||||||
            String acquirer = intent.getStringExtra("acquirer");
 | 
					            String acquirer = intent.getStringExtra("acquirer");
 | 
				
			||||||
            String mid = intent.getStringExtra("mid");
 | 
					            String mid = intent.getStringExtra("mid");
 | 
				
			||||||
            String tid = intent.getStringExtra("tid");
 | 
					            String tid = intent.getStringExtra("tid");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ EMV SPECIFIC DATA
 | 
				
			||||||
 | 
					            String emvCardholderName = intent.getStringExtra("emv_cardholder_name");
 | 
				
			||||||
 | 
					            String emvAid = intent.getStringExtra("emv_aid");
 | 
				
			||||||
 | 
					            String emvExpiry = intent.getStringExtra("emv_expiry");
 | 
				
			||||||
 | 
					            String cardNumber = intent.getStringExtra("card_number");
 | 
				
			||||||
 | 
					            String midtransResponse = intent.getStringExtra("midtrans_response");
 | 
				
			||||||
 | 
					            boolean emvMode = intent.getBooleanExtra("emv_mode", false);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ QRIS SPECIFIC DATA
 | 
				
			||||||
 | 
					            String qrString = intent.getStringExtra("qr_string");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
            // Log received data for debugging
 | 
					            // Log received data for debugging
 | 
				
			||||||
            Log.d("ReceiptActivity", "🔍 RECEIVED DATA:");
 | 
					            Log.d("ReceiptActivity", "🔍 RECEIVED DATA:");
 | 
				
			||||||
            Log.d("ReceiptActivity", "   amount: " + amount);
 | 
					            Log.d("ReceiptActivity", "   amount: " + amount);
 | 
				
			||||||
            Log.d("ReceiptActivity", "   referenceId: " + referenceId);
 | 
					            Log.d("ReceiptActivity", "   referenceId: " + referenceId);
 | 
				
			||||||
            Log.d("ReceiptActivity", "   orderId: " + orderId);
 | 
					            Log.d("ReceiptActivity", "   orderId: " + orderId);
 | 
				
			||||||
            Log.d("ReceiptActivity", "   channelCode: " + channelCode);
 | 
					 | 
				
			||||||
            Log.d("ReceiptActivity", "   acquirer (from intent): " + acquirer);
 | 
					            Log.d("ReceiptActivity", "   acquirer (from intent): " + acquirer);
 | 
				
			||||||
            Log.d("ReceiptActivity", "   createdAt: " + createdAt);
 | 
					            Log.d("ReceiptActivity", "   createdAt: " + createdAt);
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "   EMV Mode: " + emvMode);
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "   EMV Cardholder: " + emvCardholderName);
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "   Card Number: " + (cardNumber != null ? cardNumber : "N/A"));
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "   QR String Available: " + (qrString != null && !qrString.isEmpty()));
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // 1. Set merchant data with defaults
 | 
					            // 1. Set merchant data with defaults
 | 
				
			||||||
            merchantName.setText(merchantNameStr != null ? merchantNameStr : "Marcel Panjaitan");
 | 
					            merchantName.setText(merchantNameStr != null ? merchantNameStr : "TOKO KLONTONG PAK EKO");
 | 
				
			||||||
            merchantLocation.setText(merchantLocationStr != null ? merchantLocationStr : "Jakarta, Indonesia");
 | 
					            merchantLocation.setText(merchantLocationStr != null ? merchantLocationStr : "Ciputat Baru, Tangsel");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // 2. Set MID and TID
 | 
					            // 2. Set MID and TID
 | 
				
			||||||
            midText.setText(mid != null ? mid : "71000026521");
 | 
					            midText.setText("MID: " + (mid != null ? mid : "123456789901"));
 | 
				
			||||||
            tidText.setText(tid != null ? tid : "73001500");
 | 
					            tidText.setText("TID: " + (tid != null ? tid : "123456789901"));
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // 3. Set transaction number
 | 
					            // 3. Set transaction number
 | 
				
			||||||
            String displayTransactionNumber = null;
 | 
					            String displayTransactionNumber = getDisplayTransactionNumber(referenceId, transactionId, orderId);
 | 
				
			||||||
            if (referenceId != null && !referenceId.isEmpty()) {
 | 
					            transactionNumber.setText(displayTransactionNumber);
 | 
				
			||||||
                displayTransactionNumber = referenceId;
 | 
					 | 
				
			||||||
            } else if (transactionId != null && !transactionId.isEmpty()) {
 | 
					 | 
				
			||||||
                displayTransactionNumber = transactionId;
 | 
					 | 
				
			||||||
            } else if (orderId != null && !orderId.isEmpty()) {
 | 
					 | 
				
			||||||
                displayTransactionNumber = orderId;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            transactionNumber.setText(displayTransactionNumber != null ? displayTransactionNumber : "N/A");
 | 
					 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // 4. Set transaction date
 | 
					            // 4. Set transaction date
 | 
				
			||||||
            String displayDate = null;
 | 
					            String displayDate = getDisplayTransactionDate(createdAt, transactionDateStr, isEmvTransaction);
 | 
				
			||||||
            if (createdAt != null && !createdAt.isEmpty()) {
 | 
					 | 
				
			||||||
                displayDate = formatDateFromCreatedAt(createdAt);
 | 
					 | 
				
			||||||
            } else if (transactionDateStr != null && !transactionDateStr.isEmpty()) {
 | 
					 | 
				
			||||||
                displayDate = transactionDateStr;
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                displayDate = getCurrentDateTime();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            transactionDate.setText(displayDate);
 | 
					            transactionDate.setText(displayDate);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // 5. Set payment method
 | 
					            // 5. ✅ ENHANCED: Set payment method based on transaction type
 | 
				
			||||||
            String displayPaymentMethod = getPaymentMethodFromChannelCode(channelCode, paymentMethodStr);
 | 
					            String displayPaymentMethod = getDisplayPaymentMethod(channelCode, paymentMethodStr, isEmvTransaction, emvMode);
 | 
				
			||||||
            paymentMethod.setText(displayPaymentMethod);
 | 
					            paymentMethod.setText(displayPaymentMethod);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // 6. ✅ IMPROVED: Enhanced card type detection for QRIS
 | 
					            // 6. ✅ ENHANCED: Set card type with EMV and QRIS priority
 | 
				
			||||||
            String displayCardType = null;
 | 
					            String displayCardType = getDisplayCardType(cardTypeStr, acquirer, channelCode, isEmvTransaction, 
 | 
				
			||||||
            
 | 
					                                                      isQrisTransaction, midtransResponse, referenceId);
 | 
				
			||||||
            if (channelCode != null && channelCode.equalsIgnoreCase("QRIS")) {
 | 
					 | 
				
			||||||
                Log.d("ReceiptActivity", "🔍 QRIS transaction detected - searching for real acquirer");
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                // For QRIS, try to get real acquirer from webhook data
 | 
					 | 
				
			||||||
                if (referenceId != null && !referenceId.isEmpty()) {
 | 
					 | 
				
			||||||
                    String realAcquirer = fetchRealAcquirerSync(referenceId);
 | 
					 | 
				
			||||||
                    if (realAcquirer != null && !realAcquirer.isEmpty() && !realAcquirer.equalsIgnoreCase("qris")) {
 | 
					 | 
				
			||||||
                        displayCardType = getCardTypeFromAcquirer(realAcquirer, null, null);
 | 
					 | 
				
			||||||
                        Log.d("ReceiptActivity", "✅ QRIS real acquirer found: " + realAcquirer + " -> " + displayCardType);
 | 
					 | 
				
			||||||
                    } else {
 | 
					 | 
				
			||||||
                        Log.w("ReceiptActivity", "⚠️ QRIS real acquirer not found, using generic QRIS");
 | 
					 | 
				
			||||||
                        displayCardType = "QRIS";
 | 
					 | 
				
			||||||
                        
 | 
					 | 
				
			||||||
                        // Start async search for better results
 | 
					 | 
				
			||||||
                        fetchRealAcquirerFromWebhook(referenceId);
 | 
					 | 
				
			||||||
                    }
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                    displayCardType = "QRIS";
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
            } else {
 | 
					 | 
				
			||||||
                // Non-QRIS transaction
 | 
					 | 
				
			||||||
                displayCardType = getCardTypeFromAcquirer(acquirer, channelCode, cardTypeStr);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            
 | 
					 | 
				
			||||||
            cardType.setText(displayCardType);
 | 
					            cardType.setText(displayCardType);
 | 
				
			||||||
            Log.d("ReceiptActivity", "💳 FINAL CARD TYPE: " + displayCardType);
 | 
					 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // 7. Format and set amounts
 | 
					            // 7. ✅ Format and set amounts with proper calculation
 | 
				
			||||||
            setAmountData(amount, grossAmount);
 | 
					            setAmountDataEnhanced(amount, grossAmount, isEmvTransaction, isQrisTransaction);
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Log.d("ReceiptActivity", "=== TRANSACTION DATA LOADED ===");
 | 
					            Log.d("ReceiptActivity", "💳 FINAL DISPLAY VALUES:");
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "   Payment Method: " + displayPaymentMethod);
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "   Card Type: " + displayCardType);
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "   Transaction Number: " + displayTransactionNumber);
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "=== ENHANCED TRANSACTION DATA LOADED ===");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get display transaction number with priority
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String getDisplayTransactionNumber(String referenceId, String transactionId, String orderId) {
 | 
				
			||||||
 | 
					        if (referenceId != null && !referenceId.isEmpty()) {
 | 
				
			||||||
 | 
					            return referenceId;
 | 
				
			||||||
 | 
					        } else if (transactionId != null && !transactionId.isEmpty()) {
 | 
				
			||||||
 | 
					            // For long transaction IDs, show last 10 characters
 | 
				
			||||||
 | 
					            return transactionId.length() > 10 ? 
 | 
				
			||||||
 | 
					                transactionId.substring(transactionId.length() - 10) : transactionId;
 | 
				
			||||||
 | 
					        } else if (orderId != null && !orderId.isEmpty()) {
 | 
				
			||||||
 | 
					            return orderId.length() > 10 ? 
 | 
				
			||||||
 | 
					                orderId.substring(orderId.length() - 10) : orderId;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return String.valueOf(System.currentTimeMillis() % 10000000000L);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get display transaction date with proper formatting
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String getDisplayTransactionDate(String createdAt, String transactionDateStr, boolean isEmvTransaction) {
 | 
				
			||||||
 | 
					        String dateToFormat = null;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (createdAt != null && !createdAt.isEmpty()) {
 | 
				
			||||||
 | 
					            dateToFormat = createdAt;
 | 
				
			||||||
 | 
					        } else if (transactionDateStr != null && !transactionDateStr.isEmpty()) {
 | 
				
			||||||
 | 
					            dateToFormat = transactionDateStr;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (dateToFormat != null) {
 | 
				
			||||||
 | 
					            if (isEmvTransaction) {
 | 
				
			||||||
 | 
					                return formatDateForEmvTransaction(dateToFormat);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return formatDateFromCreatedAt(dateToFormat);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return getCurrentDateTime();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Format date specifically for EMV transactions
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String formatDateForEmvTransaction(String dateString) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // EMV transactions might use different date formats
 | 
				
			||||||
 | 
					            String[] inputFormats = {
 | 
				
			||||||
 | 
					                "dd MMMM yyyy HH:mm",
 | 
				
			||||||
 | 
					                "yyyy-MM-dd HH:mm:ss",
 | 
				
			||||||
 | 
					                "yyyy-MM-dd'T'HH:mm:ss'Z'",
 | 
				
			||||||
 | 
					                "dd/MM/yyyy HH:mm"
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            SimpleDateFormat outputFormat = new SimpleDateFormat("dd MMMM yyyy HH:mm", new Locale("id", "ID"));
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            for (String format : inputFormats) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    SimpleDateFormat inputFormat = new SimpleDateFormat(format, 
 | 
				
			||||||
 | 
					                        format.contains("MMMM") ? new Locale("id", "ID") : Locale.getDefault());
 | 
				
			||||||
 | 
					                    Date date = inputFormat.parse(dateString);
 | 
				
			||||||
 | 
					                    String formatted = outputFormat.format(date);
 | 
				
			||||||
 | 
					                    Log.d("ReceiptActivity", "EMV Date formatting: '" + dateString + "' -> '" + formatted + "'");
 | 
				
			||||||
 | 
					                    return formatted;
 | 
				
			||||||
 | 
					                } catch (Exception ignored) {
 | 
				
			||||||
 | 
					                    // Try next format
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // If all formats fail, return as-is
 | 
				
			||||||
 | 
					            Log.w("ReceiptActivity", "Could not format EMV date: " + dateString);
 | 
				
			||||||
 | 
					            return dateString;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e("ReceiptActivity", "Error formatting EMV date: " + dateString, e);
 | 
				
			||||||
 | 
					            return dateString;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -225,7 +323,7 @@ public class ReceiptActivity extends AppCompatActivity {
 | 
				
			|||||||
            SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
					            SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // Output format for receipt: "dd/MM/yyyy HH:mm"
 | 
					            // Output format for receipt: "dd/MM/yyyy HH:mm"
 | 
				
			||||||
            SimpleDateFormat outputFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm", new Locale("id", "ID"));
 | 
					            SimpleDateFormat outputFormat = new SimpleDateFormat("dd MMMM yyyy HH:mm", new Locale("id", "ID"));
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            Date date = inputFormat.parse(createdAt);
 | 
					            Date date = inputFormat.parse(createdAt);
 | 
				
			||||||
            String formatted = outputFormat.format(date);
 | 
					            String formatted = outputFormat.format(date);
 | 
				
			||||||
@ -240,6 +338,39 @@ public class ReceiptActivity extends AppCompatActivity {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get display payment method with EMV support
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String getDisplayPaymentMethod(String channelCode, String fallbackPaymentMethod, 
 | 
				
			||||||
 | 
					                                         boolean isEmvTransaction, boolean emvMode) {
 | 
				
			||||||
 | 
					        if (isEmvTransaction) {
 | 
				
			||||||
 | 
					            // For EMV transactions, be more specific
 | 
				
			||||||
 | 
					            if (emvMode) {
 | 
				
			||||||
 | 
					                if (channelCode != null) {
 | 
				
			||||||
 | 
					                    switch (channelCode.toUpperCase()) {
 | 
				
			||||||
 | 
					                        case "CREDIT": return "Kartu Kredit (EMV)";
 | 
				
			||||||
 | 
					                        case "DEBIT": return "Kartu Debit (EMV)";
 | 
				
			||||||
 | 
					                        default: return "Kartu Kredit (EMV)";
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return "Kartu Kredit (EMV)";
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                // Magnetic stripe
 | 
				
			||||||
 | 
					                if (channelCode != null) {
 | 
				
			||||||
 | 
					                    switch (channelCode.toUpperCase()) {
 | 
				
			||||||
 | 
					                        case "CREDIT": return "Kartu Kredit";
 | 
				
			||||||
 | 
					                        case "DEBIT": return "Kartu Debit";
 | 
				
			||||||
 | 
					                        default: return "Kartu Kredit";
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return "Kartu Kredit";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // For QRIS and other transactions, use existing logic
 | 
				
			||||||
 | 
					            return getPaymentMethodFromChannelCode(channelCode, fallbackPaymentMethod);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Get payment method name from channel_code with comprehensive mapping
 | 
					     * Get payment method name from channel_code with comprehensive mapping
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
@ -289,6 +420,78 @@ public class ReceiptActivity extends AppCompatActivity {
 | 
				
			|||||||
        return fallbackPaymentMethod != null ? fallbackPaymentMethod : "QRIS";
 | 
					        return fallbackPaymentMethod != null ? fallbackPaymentMethod : "QRIS";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * ✅ ENHANCED: Card type detection with EMV and QRIS priority
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String getDisplayCardType(String cardTypeStr, String acquirer, String channelCode, 
 | 
				
			||||||
 | 
					                                    boolean isEmvTransaction, boolean isQrisTransaction, 
 | 
				
			||||||
 | 
					                                    String midtransResponse, String referenceId) {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d("ReceiptActivity", "🔍 ENHANCED CARD TYPE DETECTION:");
 | 
				
			||||||
 | 
					        Log.d("ReceiptActivity", "   Input Card Type: " + cardTypeStr);
 | 
				
			||||||
 | 
					        Log.d("ReceiptActivity", "   Input Acquirer: " + acquirer);
 | 
				
			||||||
 | 
					        Log.d("ReceiptActivity", "   Is EMV Transaction: " + isEmvTransaction);
 | 
				
			||||||
 | 
					        Log.d("ReceiptActivity", "   Is QRIS Transaction: " + isQrisTransaction);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (isEmvTransaction) {
 | 
				
			||||||
 | 
					            // ✅ FOR EMV TRANSACTIONS: Priority to cardTypeStr from ResultTransactionActivity
 | 
				
			||||||
 | 
					            if (cardTypeStr != null && !cardTypeStr.isEmpty() && !cardTypeStr.equalsIgnoreCase("unknown")) {
 | 
				
			||||||
 | 
					                Log.d("ReceiptActivity", "✅ EMV: Using provided card type: " + cardTypeStr);
 | 
				
			||||||
 | 
					                return cardTypeStr;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ FALLBACK: Try to extract from Midtrans response
 | 
				
			||||||
 | 
					            if (midtransResponse != null && !midtransResponse.isEmpty()) {
 | 
				
			||||||
 | 
					                String bankFromMidtrans = extractBankFromMidtransResponse(midtransResponse);
 | 
				
			||||||
 | 
					                if (bankFromMidtrans != null && !bankFromMidtrans.isEmpty()) {
 | 
				
			||||||
 | 
					                    Log.d("ReceiptActivity", "✅ EMV: Using bank from Midtrans response: " + bankFromMidtrans);
 | 
				
			||||||
 | 
					                    return bankFromMidtrans;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ FALLBACK: Use acquirer
 | 
				
			||||||
 | 
					            if (acquirer != null && !acquirer.isEmpty() && !acquirer.equalsIgnoreCase("qris")) {
 | 
				
			||||||
 | 
					                String mappedAcquirer = getCardTypeFromAcquirer(acquirer, channelCode, null);
 | 
				
			||||||
 | 
					                Log.d("ReceiptActivity", "✅ EMV: Using mapped acquirer: " + mappedAcquirer);
 | 
				
			||||||
 | 
					                return mappedAcquirer;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "⚠️ EMV: Using default fallback: BCA");
 | 
				
			||||||
 | 
					            return "BCA"; // Default for EMV
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } else if (isQrisTransaction) {
 | 
				
			||||||
 | 
					            // ✅ FOR QRIS TRANSACTIONS: Enhanced detection
 | 
				
			||||||
 | 
					            Log.d("ReceiptActivity", "🔍 QRIS transaction detected - searching for real acquirer");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Priority 1: Use provided cardTypeStr if it's not generic
 | 
				
			||||||
 | 
					            if (cardTypeStr != null && !cardTypeStr.isEmpty() && 
 | 
				
			||||||
 | 
					                !cardTypeStr.equalsIgnoreCase("qris") && !cardTypeStr.equalsIgnoreCase("unknown")) {
 | 
				
			||||||
 | 
					                Log.d("ReceiptActivity", "✅ QRIS: Using provided specific card type: " + cardTypeStr);
 | 
				
			||||||
 | 
					                return cardTypeStr;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Priority 2: Search webhook logs for real acquirer
 | 
				
			||||||
 | 
					            if (referenceId != null && !referenceId.isEmpty()) {
 | 
				
			||||||
 | 
					                String realAcquirer = fetchRealAcquirerSync(referenceId);
 | 
				
			||||||
 | 
					                if (realAcquirer != null && !realAcquirer.isEmpty() && !realAcquirer.equalsIgnoreCase("qris")) {
 | 
				
			||||||
 | 
					                    String mappedQrisAcquirer = getCardTypeFromAcquirer(realAcquirer, null, null);
 | 
				
			||||||
 | 
					                    Log.d("ReceiptActivity", "✅ QRIS real acquirer found: " + realAcquirer + " -> " + mappedQrisAcquirer);
 | 
				
			||||||
 | 
					                    return mappedQrisAcquirer;
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Log.w("ReceiptActivity", "⚠️ QRIS real acquirer not found, using generic QRIS");
 | 
				
			||||||
 | 
					                    // Start async search for better results
 | 
				
			||||||
 | 
					                    fetchRealAcquirerFromWebhook(referenceId);
 | 
				
			||||||
 | 
					                    return "QRIS";
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                return "QRIS";
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // ✅ FOR OTHER TRANSACTIONS: Use standard logic
 | 
				
			||||||
 | 
					            return getCardTypeFromAcquirer(acquirer, channelCode, cardTypeStr);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private String getCardTypeFromAcquirer(String acquirer, String channelCode, String fallbackCardType) {
 | 
					    private String getCardTypeFromAcquirer(String acquirer, String channelCode, String fallbackCardType) {
 | 
				
			||||||
        // STEP 1: If we have a valid acquirer that's not generic "qris", use it
 | 
					        // STEP 1: If we have a valid acquirer that's not generic "qris", use it
 | 
				
			||||||
        if (acquirer != null && !acquirer.isEmpty() && !acquirer.equalsIgnoreCase("qris")) {
 | 
					        if (acquirer != null && !acquirer.isEmpty() && !acquirer.equalsIgnoreCase("qris")) {
 | 
				
			||||||
@ -297,88 +500,14 @@ public class ReceiptActivity extends AppCompatActivity {
 | 
				
			|||||||
            Log.d("ReceiptActivity", "🔍 Mapping acquirer: '" + acquirer + "' -> '" + acq + "'");
 | 
					            Log.d("ReceiptActivity", "🔍 Mapping acquirer: '" + acquirer + "' -> '" + acq + "'");
 | 
				
			||||||
            
 | 
					            
 | 
				
			||||||
            // ✅ COMPREHENSIVE acquirer mapping (case-insensitive)
 | 
					            // ✅ COMPREHENSIVE acquirer mapping (case-insensitive)
 | 
				
			||||||
 | 
					            String mappedName = ISSUER_DISPLAY_MAP.get(acq);
 | 
				
			||||||
 | 
					            if (mappedName != null) {
 | 
				
			||||||
 | 
					                Log.d("ReceiptActivity", "✅ Mapped acquirer: " + acquirer + " -> " + mappedName);
 | 
				
			||||||
 | 
					                return mappedName;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Additional mapping for variations not in the map
 | 
				
			||||||
            switch (acq) {
 | 
					            switch (acq) {
 | 
				
			||||||
                // E-Wallet acquirers (most common for QRIS)
 | 
					 | 
				
			||||||
                case "gopay": 
 | 
					 | 
				
			||||||
                case "go-pay": 
 | 
					 | 
				
			||||||
                case "gojek": return "GoPay";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "shopeepay": 
 | 
					 | 
				
			||||||
                case "shopee_pay": 
 | 
					 | 
				
			||||||
                case "shopee": return "ShopeePay";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "ovo": return "OVO";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "dana": return "DANA";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "linkaja": 
 | 
					 | 
				
			||||||
                case "link_aja": 
 | 
					 | 
				
			||||||
                case "tcash": return "LinkAja";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "jenius": 
 | 
					 | 
				
			||||||
                case "btpn": return "Jenius";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "kaspro": 
 | 
					 | 
				
			||||||
                case "kas_pro": return "KasPro";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "sakuku": 
 | 
					 | 
				
			||||||
                case "saku_ku": return "SakuKu";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "doku": 
 | 
					 | 
				
			||||||
                case "doku_wallet": return "DOKU";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "paymi": 
 | 
					 | 
				
			||||||
                case "pay_mi": return "PayMi";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "isaku": 
 | 
					 | 
				
			||||||
                case "i_saku": return "i.Saku";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                // Bank acquirers
 | 
					 | 
				
			||||||
                case "bca": 
 | 
					 | 
				
			||||||
                case "bank_bca": return "BCA";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "mandiri":
 | 
					 | 
				
			||||||
                case "bank_mandiri":
 | 
					 | 
				
			||||||
                case "mandiri_bill": return "Mandiri";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "bni":
 | 
					 | 
				
			||||||
                case "bank_bni":
 | 
					 | 
				
			||||||
                case "bni_va": return "BNI";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "bri":
 | 
					 | 
				
			||||||
                case "bank_bri":
 | 
					 | 
				
			||||||
                case "bri_va": return "BRI";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "permata":
 | 
					 | 
				
			||||||
                case "bank_permata":
 | 
					 | 
				
			||||||
                case "permata_va": return "Permata";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "cimb":
 | 
					 | 
				
			||||||
                case "cimb_niaga":
 | 
					 | 
				
			||||||
                case "bank_cimb":
 | 
					 | 
				
			||||||
                case "cimb_va": return "CIMB Niaga";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "danamon":
 | 
					 | 
				
			||||||
                case "bank_danamon":
 | 
					 | 
				
			||||||
                case "danamon_va": return "Danamon";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "bsi":
 | 
					 | 
				
			||||||
                case "bank_bsi":
 | 
					 | 
				
			||||||
                case "bsi_va": 
 | 
					 | 
				
			||||||
                case "syariah_indonesia": return "BSI";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "maybank":
 | 
					 | 
				
			||||||
                case "bank_maybank": return "Maybank";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "bca_digital":
 | 
					 | 
				
			||||||
                case "blu": return "BCA Digital";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "jago":
 | 
					 | 
				
			||||||
                case "bank_jago": return "Bank Jago";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                case "seabank":
 | 
					 | 
				
			||||||
                case "sea_bank": return "SeaBank";
 | 
					 | 
				
			||||||
                
 | 
					 | 
				
			||||||
                // Credit card acquirers
 | 
					                // Credit card acquirers
 | 
				
			||||||
                case "visa": return "Visa";
 | 
					                case "visa": return "Visa";
 | 
				
			||||||
                case "mastercard": 
 | 
					                case "mastercard": 
 | 
				
			||||||
@ -738,6 +867,78 @@ public class ReceiptActivity extends AppCompatActivity {
 | 
				
			|||||||
        return null; // No acquirer found for specified criteria
 | 
					        return null; // No acquirer found for specified criteria
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Extract bank from Midtrans response JSON string
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String extractBankFromMidtransResponse(String midtransResponse) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            org.json.JSONObject response = new org.json.JSONObject(midtransResponse);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Try different possible bank fields
 | 
				
			||||||
 | 
					            String[] bankFields = {"bank", "issuer", "acquiring_bank", "issuer_bank"};
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            for (String field : bankFields) {
 | 
				
			||||||
 | 
					                if (response.has(field)) {
 | 
				
			||||||
 | 
					                    String bankValue = response.getString(field);
 | 
				
			||||||
 | 
					                    if (bankValue != null && !bankValue.trim().isEmpty() && !bankValue.equalsIgnoreCase("qris")) {
 | 
				
			||||||
 | 
					                        Log.d("ReceiptActivity", "Found bank in Midtrans response (" + field + "): " + bankValue);
 | 
				
			||||||
 | 
					                        return formatBankNameForReceipt(bankValue);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Log.w("ReceiptActivity", "No valid bank found in Midtrans response");
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e("ReceiptActivity", "Error extracting bank from Midtrans response: " + e.getMessage());
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Format bank name specifically for receipt display
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String formatBankNameForReceipt(String bankName) {
 | 
				
			||||||
 | 
					        if (bankName == null || bankName.trim().isEmpty()) {
 | 
				
			||||||
 | 
					            return "BCA"; // Default
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        String formatted = bankName.trim();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Common bank name mappings for receipt display
 | 
				
			||||||
 | 
					        switch (formatted.toUpperCase()) {
 | 
				
			||||||
 | 
					            case "BCA":
 | 
				
			||||||
 | 
					            case "BANK BCA":
 | 
				
			||||||
 | 
					            case "BANK CENTRAL ASIA": return "BCA";
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            case "MANDIRI":
 | 
				
			||||||
 | 
					            case "BANK MANDIRI": return "Mandiri";
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            case "BNI":
 | 
				
			||||||
 | 
					            case "BANK BNI":
 | 
				
			||||||
 | 
					            case "BANK NEGARA INDONESIA": return "BNI";
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            case "BRI":
 | 
				
			||||||
 | 
					            case "BANK BRI":
 | 
				
			||||||
 | 
					            case "BANK RAKYAT INDONESIA": return "BRI";
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            case "CIMB":
 | 
				
			||||||
 | 
					            case "CIMB NIAGA":
 | 
				
			||||||
 | 
					            case "BANK CIMB NIAGA": return "CIMB Niaga";
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            case "DANAMON":
 | 
				
			||||||
 | 
					            case "BANK DANAMON": return "Danamon";
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            case "PERMATA":
 | 
				
			||||||
 | 
					            case "BANK PERMATA": return "Permata";
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                // Return capitalized version
 | 
				
			||||||
 | 
					                return capitalizeFirstLetter(formatted);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private String capitalizeFirstLetter(String input) {
 | 
					    private String capitalizeFirstLetter(String input) {
 | 
				
			||||||
        if (input == null || input.isEmpty()) {
 | 
					        if (input == null || input.isEmpty()) {
 | 
				
			||||||
            return input;
 | 
					            return input;
 | 
				
			||||||
@ -756,51 +957,79 @@ public class ReceiptActivity extends AppCompatActivity {
 | 
				
			|||||||
        return cleaned.substring(0, 1).toUpperCase() + cleaned.substring(1).toLowerCase();
 | 
					        return cleaned.substring(0, 1).toUpperCase() + cleaned.substring(1).toLowerCase();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private void setAmountData(String amount, String grossAmount) {
 | 
					    /**
 | 
				
			||||||
        // Prioritize 'amount' over 'grossAmount' for transaction data
 | 
					     * ✅ ENHANCED: Set amount data with EMV and QRIS support
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private void setAmountDataEnhanced(String amount, String grossAmount, boolean isEmvTransaction, boolean isQrisTransaction) {
 | 
				
			||||||
        String amountToUse = amount != null ? amount : grossAmount;
 | 
					        String amountToUse = amount != null ? amount : grossAmount;
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        Log.d("ReceiptActivity", "Setting amount data - amount: " + amount + 
 | 
					        Log.d("ReceiptActivity", "Setting enhanced amount data - amount: " + amount + 
 | 
				
			||||||
            ", grossAmount: " + grossAmount + ", using: " + amountToUse);
 | 
					            ", grossAmount: " + grossAmount + ", using: " + amountToUse + 
 | 
				
			||||||
 | 
					            ", isEMV: " + isEmvTransaction + ", isQRIS: " + isQrisTransaction);
 | 
				
			||||||
        
 | 
					        
 | 
				
			||||||
        if (amountToUse != null) {
 | 
					        if (amountToUse != null) {
 | 
				
			||||||
            try {
 | 
					            try {
 | 
				
			||||||
                // Clean and parse the amount
 | 
					 | 
				
			||||||
                String cleanAmount = cleanAmountString(amountToUse);
 | 
					                String cleanAmount = cleanAmountString(amountToUse);
 | 
				
			||||||
                Log.d("ReceiptActivity", "Cleaned amount: " + cleanAmount);
 | 
					                Log.d("ReceiptActivity", "Cleaned amount: " + cleanAmount);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // Parse as long integer (Indonesian Rupiah doesn't use decimal cents)
 | 
					 | 
				
			||||||
                long amountLong = Long.parseLong(cleanAmount);
 | 
					                long amountLong = Long.parseLong(cleanAmount);
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // Set transaction total
 | 
					                // Set transaction total
 | 
				
			||||||
                transactionTotal.setText("Rp " + formatCurrency(amountLong));
 | 
					                transactionTotal.setText(formatCurrency(amountLong));
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // Calculate tax and service fee (for QRIS, typically no additional fees)
 | 
					                // ✅ CALCULATE FEES BASED ON TRANSACTION TYPE
 | 
				
			||||||
                long tax = 0; // QRIS usually doesn't have tax
 | 
					                long tax = 0;
 | 
				
			||||||
                long serviceFeeValue = 0; // QRIS usually doesn't have service fee
 | 
					                long serviceFeeValue = 0;
 | 
				
			||||||
                long total = amountLong + tax + serviceFeeValue;
 | 
					                long total = amountLong;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (isEmvTransaction) {
 | 
				
			||||||
 | 
					                    // For EMV transactions, check if gross amount includes additional fees
 | 
				
			||||||
 | 
					                    if (grossAmount != null && !grossAmount.equals(amount)) {
 | 
				
			||||||
 | 
					                        try {
 | 
				
			||||||
 | 
					                            long grossAmountLong = Long.parseLong(cleanAmountString(grossAmount));
 | 
				
			||||||
 | 
					                            long difference = grossAmountLong - amountLong;
 | 
				
			||||||
 | 
					                            
 | 
				
			||||||
 | 
					                            if (difference > 0) {
 | 
				
			||||||
 | 
					                                // Assume 11% tax and 500 service fee (adjust based on your business logic)
 | 
				
			||||||
 | 
					                                tax = Math.round(amountLong * 0.11);
 | 
				
			||||||
 | 
					                                serviceFeeValue = 500;
 | 
				
			||||||
 | 
					                                total = grossAmountLong;
 | 
				
			||||||
 | 
					                                
 | 
				
			||||||
 | 
					                                Log.d("ReceiptActivity", "EMV: Calculated tax=" + tax + ", service=" + serviceFeeValue + ", total=" + total);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } catch (Exception e) {
 | 
				
			||||||
 | 
					                            Log.w("ReceiptActivity", "Could not parse gross amount for EMV: " + grossAmount);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } else if (isQrisTransaction) {
 | 
				
			||||||
 | 
					                    // For QRIS, typically no additional fees (tax=0, service=0)
 | 
				
			||||||
 | 
					                    tax = 0;
 | 
				
			||||||
 | 
					                    serviceFeeValue = 0;
 | 
				
			||||||
 | 
					                    total = amountLong;
 | 
				
			||||||
 | 
					                    Log.d("ReceiptActivity", "QRIS: No additional fees - total=" + total);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                // Set calculated values
 | 
					                // Set calculated values
 | 
				
			||||||
                taxPercentage.setText("Rp 0");
 | 
					                taxPercentage.setText(tax > 0 ? "11%" : "0%");
 | 
				
			||||||
                serviceFee.setText("Rp 0");
 | 
					                serviceFee.setText(formatCurrency(serviceFeeValue));
 | 
				
			||||||
                finalTotal.setText("Rp " + formatCurrency(total));
 | 
					                finalTotal.setText(formatCurrency(total));
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
                Log.d("ReceiptActivity", "Amount formatting successful: " + amountLong + " -> Rp " + formatCurrency(total));
 | 
					                Log.d("ReceiptActivity", "Enhanced amount formatting successful: " + amountLong + " -> " + formatCurrency(total));
 | 
				
			||||||
                
 | 
					                
 | 
				
			||||||
            } catch (NumberFormatException e) {
 | 
					            } catch (NumberFormatException e) {
 | 
				
			||||||
                Log.e("ReceiptActivity", "Error parsing amount: " + amountToUse, e);
 | 
					                Log.e("ReceiptActivity", "Error parsing enhanced amount: " + amountToUse, e);
 | 
				
			||||||
                // Fallback if parsing fails
 | 
					                // Fallback if parsing fails
 | 
				
			||||||
                transactionTotal.setText("Rp " + amountToUse);
 | 
					                transactionTotal.setText(amountToUse);
 | 
				
			||||||
                taxPercentage.setText("Rp 0");
 | 
					                taxPercentage.setText("0%");
 | 
				
			||||||
                serviceFee.setText("Rp 0");
 | 
					                serviceFee.setText("0");
 | 
				
			||||||
                finalTotal.setText("Rp " + amountToUse);
 | 
					                finalTotal.setText(amountToUse);
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            // Default values if no amount provided
 | 
					            // Default values if no amount provided
 | 
				
			||||||
            transactionTotal.setText("Rp 0");
 | 
					            transactionTotal.setText("0");
 | 
				
			||||||
            taxPercentage.setText("Rp 0");
 | 
					            taxPercentage.setText("0%");
 | 
				
			||||||
            serviceFee.setText("Rp 0");
 | 
					            serviceFee.setText("0");
 | 
				
			||||||
            finalTotal.setText("Rp 0");
 | 
					            finalTotal.setText("0");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -874,7 +1103,7 @@ public class ReceiptActivity extends AppCompatActivity {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private String getCurrentDateTime() {
 | 
					    private String getCurrentDateTime() {
 | 
				
			||||||
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm", new Locale("id", "ID"));
 | 
					        SimpleDateFormat sdf = new SimpleDateFormat("dd MMMM yyyy HH:mm", new Locale("id", "ID"));
 | 
				
			||||||
        return sdf.format(new Date());
 | 
					        return sdf.format(new Date());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -902,9 +1131,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;
 | 
				
			||||||
@ -914,6 +1143,11 @@ public class ReceiptActivity extends AppCompatActivity {
 | 
				
			|||||||
                    navigateToHomePage();
 | 
					                    navigateToHomePage();
 | 
				
			||||||
                    break;
 | 
					                    break;
 | 
				
			||||||
                    
 | 
					                    
 | 
				
			||||||
 | 
					                case "ResultTransactionActivity":
 | 
				
			||||||
 | 
					                    // ✅ NEW: Handle back from ResultTransactionActivity
 | 
				
			||||||
 | 
					                    navigateToHomePage();
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
                case "PaymentActivity":
 | 
					                case "PaymentActivity":
 | 
				
			||||||
                case "QrisActivity":
 | 
					                case "QrisActivity":
 | 
				
			||||||
                    // Go back to payment/qris activity
 | 
					                    // Go back to payment/qris activity
 | 
				
			||||||
 | 
				
			|||||||
@ -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;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										145
									
								
								app/src/main/java/com/example/bdkipoc/emv/EmvTTS.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								app/src/main/java/com/example/bdkipoc/emv/EmvTTS.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,145 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.emv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.speech.tts.TextToSpeech;
 | 
				
			||||||
 | 
					import android.speech.tts.UtteranceProgressListener;
 | 
				
			||||||
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.MyApplication;
 | 
				
			||||||
 | 
					import com.example.bdkipoc.utils.LogUtil;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Locale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public final class EmvTTS extends UtteranceProgressListener {
 | 
				
			||||||
 | 
					    private static final String TAG = "EmvTTS";
 | 
				
			||||||
 | 
					    private TextToSpeech textToSpeech;
 | 
				
			||||||
 | 
					    private boolean supportTTS;
 | 
				
			||||||
 | 
					    private ITTSProgressListener listener;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private EmvTTS() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static EmvTTS getInstance() {
 | 
				
			||||||
 | 
					        return SingletonHolder.INSTANCE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void setTTSListener(ITTSProgressListener l) {
 | 
				
			||||||
 | 
					        listener = l;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void removeTTSListener() {
 | 
				
			||||||
 | 
					        listener = null;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static final class SingletonHolder {
 | 
				
			||||||
 | 
					        private static final EmvTTS INSTANCE = new EmvTTS();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void init() {
 | 
				
			||||||
 | 
					        //初始化TTS对象
 | 
				
			||||||
 | 
					        destroy();
 | 
				
			||||||
 | 
					        textToSpeech = new TextToSpeech(MyApplication.app, this::onTTSInit);
 | 
				
			||||||
 | 
					        textToSpeech.setOnUtteranceProgressListener(this);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void play(String text) {
 | 
				
			||||||
 | 
					        play(text, "0");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void play(String text, String utteranceId) {
 | 
				
			||||||
 | 
					        if (!supportTTS) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "PinPadTTS: play TTS failed, TTS not support...");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (textToSpeech == null) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "PinPadTTS: play TTS slipped, textToSpeech not init..");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Log.e(TAG, "play() text: [" + text + "]");
 | 
				
			||||||
 | 
					        textToSpeech.speak(text, TextToSpeech.QUEUE_FLUSH, null, utteranceId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onStart(String utteranceId) {
 | 
				
			||||||
 | 
					        Log.e(TAG, "播放开始,utteranceId:" + utteranceId);
 | 
				
			||||||
 | 
					        if (listener != null) {
 | 
				
			||||||
 | 
					            listener.onStart(utteranceId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onDone(String utteranceId) {
 | 
				
			||||||
 | 
					        Log.e(TAG, "播放结束,utteranceId:" + utteranceId);
 | 
				
			||||||
 | 
					        if (listener != null) {
 | 
				
			||||||
 | 
					            listener.onDone(utteranceId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onError(String utteranceId) {
 | 
				
			||||||
 | 
					        Log.e(TAG, "播放出错,utteranceId:" + utteranceId);
 | 
				
			||||||
 | 
					        if (listener != null) {
 | 
				
			||||||
 | 
					            listener.onError(utteranceId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onStop(String utteranceId, boolean interrupted) {
 | 
				
			||||||
 | 
					        Log.e(TAG, "播放停止,utteranceId:" + utteranceId + ",interrupted:" + interrupted);
 | 
				
			||||||
 | 
					        if (listener != null) {
 | 
				
			||||||
 | 
					            listener.onStop(utteranceId, interrupted);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void stop() {
 | 
				
			||||||
 | 
					        if (textToSpeech != null) {
 | 
				
			||||||
 | 
					            int code = textToSpeech.stop();
 | 
				
			||||||
 | 
					            Log.e(TAG, "tts stop() code:" + code);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    boolean isSpeaking() {
 | 
				
			||||||
 | 
					        if (textToSpeech != null) {
 | 
				
			||||||
 | 
					            return textToSpeech.isSpeaking();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void destroy() {
 | 
				
			||||||
 | 
					        if (textToSpeech != null) {
 | 
				
			||||||
 | 
					            textToSpeech.stop();
 | 
				
			||||||
 | 
					            textToSpeech.shutdown();
 | 
				
			||||||
 | 
					            textToSpeech = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** TTS初始化回调 */
 | 
				
			||||||
 | 
					    private void onTTSInit(int status) {
 | 
				
			||||||
 | 
					        if (status != TextToSpeech.SUCCESS) {
 | 
				
			||||||
 | 
					            LogUtil.e(TAG, "PinPadTTS: init TTS failed, status:" + status);
 | 
				
			||||||
 | 
					            supportTTS = false;
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        updateTtsLanguage();
 | 
				
			||||||
 | 
					        if (supportTTS) {
 | 
				
			||||||
 | 
					            textToSpeech.setPitch(1.0f);
 | 
				
			||||||
 | 
					            textToSpeech.setSpeechRate(1.0f);
 | 
				
			||||||
 | 
					            LogUtil.e(TAG, "onTTSInit() success,locale:" + textToSpeech.getVoice().getLocale());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** 更新TTS语言 */
 | 
				
			||||||
 | 
					    private void updateTtsLanguage() {
 | 
				
			||||||
 | 
					        Locale locale = Locale.ENGLISH;
 | 
				
			||||||
 | 
					        int result = textToSpeech.setLanguage(locale);
 | 
				
			||||||
 | 
					        if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
 | 
				
			||||||
 | 
					            supportTTS = false; //系统不支持当前Locale对应的语音播报
 | 
				
			||||||
 | 
					            LogUtil.e(TAG, "updateTtsLanguage() failed, TTS not support in locale:" + locale);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            supportTTS = true;
 | 
				
			||||||
 | 
					            LogUtil.e(TAG, "updateTtsLanguage() success, TTS locale:" + locale);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.emv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.speech.tts.TextToSpeech;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public interface ITTSProgressListener {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called when an utterance "starts" as perceived by the caller. This will
 | 
				
			||||||
 | 
					     * be soon before audio is played back in the case of a {@link TextToSpeech#speak}
 | 
				
			||||||
 | 
					     * or before the first bytes of a file are written to the file system in the case
 | 
				
			||||||
 | 
					     * of {@link TextToSpeech#synthesizeToFile}.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param utteranceId The utterance ID of the utterance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void onStart(String utteranceId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called when an utterance has successfully completed processing.
 | 
				
			||||||
 | 
					     * All audio will have been played back by this point for audible output, and all
 | 
				
			||||||
 | 
					     * output will have been written to disk for file synthesis requests.
 | 
				
			||||||
 | 
					     * <p>
 | 
				
			||||||
 | 
					     * This request is guaranteed to be called after {@link #onStart(String)}.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param utteranceId The utterance ID of the utterance.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void onDone(String utteranceId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called when an error has occurred during processing. This can be called
 | 
				
			||||||
 | 
					     * at any point in the synthesis process. Note that there might be calls
 | 
				
			||||||
 | 
					     * to {@link #onStart(String)} for specified utteranceId but there will never
 | 
				
			||||||
 | 
					     * be a call to both {@link #onDone(String)} and {@link #onError(String)} for
 | 
				
			||||||
 | 
					     * the same utterance.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param utteranceId The utterance ID of the utterance.
 | 
				
			||||||
 | 
					     * @deprecated Use {@link #onError(String, int)} instead
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * @deprecated Use {@link #onError(String, int)} instead
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Deprecated
 | 
				
			||||||
 | 
					    void onError(String utteranceId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Called when an utterance has been stopped while in progress or flushed from the
 | 
				
			||||||
 | 
					     * synthesis queue. This can happen if a client calls {@link TextToSpeech#stop()}
 | 
				
			||||||
 | 
					     * or uses {@link TextToSpeech#QUEUE_FLUSH} as an argument with the
 | 
				
			||||||
 | 
					     * {@link TextToSpeech#speak} or {@link TextToSpeech#synthesizeToFile} methods.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param utteranceId The utterance ID of the utterance.
 | 
				
			||||||
 | 
					     * @param interrupted If true, then the utterance was interrupted while being synthesized
 | 
				
			||||||
 | 
					     *                    and its output is incomplete. If false, then the utterance was flushed
 | 
				
			||||||
 | 
					     *                    before the synthesis started.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    void onStop(String utteranceId, boolean interrupted);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -70,7 +70,7 @@ public class QrisActivity extends AppCompatActivity {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private static final String BACKEND_BASE = "https://be-edc.msvc.app";
 | 
					    private static final String BACKEND_BASE = "https://be-edc.msvc.app";
 | 
				
			||||||
    private static final String MIDTRANS_CHARGE_URL = "https://api.sandbox.midtrans.com/v2/charge";
 | 
					    private static final String MIDTRANS_CHARGE_URL = "https://api.sandbox.midtrans.com/v2/charge";
 | 
				
			||||||
    private static final String MIDTRANS_AUTH = "Basic U0ItTWlkLXNlcnZlci1JM2RJWXdIRzVuamVMeHJCMVZ5endWMUM="; // Replace with your actual key
 | 
					    private static final String MIDTRANS_AUTH = "Basic U0ItTWlkLXNlcnZlci1PM2t1bXkwVDl4M1VvYnVvVTc3NW5QbXc=";
 | 
				
			||||||
    private static final String WEBHOOK_URL = "https://be-edc.msvc.app/webhooks/midtrans";
 | 
					    private static final String WEBHOOK_URL = "https://be-edc.msvc.app/webhooks/midtrans";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @Override
 | 
					    @Override
 | 
				
			||||||
							
								
								
									
										1448
									
								
								app/src/main/java/com/example/bdkipoc/qris/QrisResultActivity.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1448
									
								
								app/src/main/java/com/example/bdkipoc/qris/QrisResultActivity.java
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -0,0 +1,711 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.transaction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Intent;
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.os.Handler;
 | 
				
			||||||
 | 
					import android.os.Looper;
 | 
				
			||||||
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					import android.view.View;
 | 
				
			||||||
 | 
					import android.widget.Button;
 | 
				
			||||||
 | 
					import android.widget.ImageView;
 | 
				
			||||||
 | 
					import android.widget.LinearLayout;
 | 
				
			||||||
 | 
					import android.widget.TextView;
 | 
				
			||||||
 | 
					import android.widget.Toast;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import androidx.appcompat.app.AlertDialog;
 | 
				
			||||||
 | 
					import androidx.appcompat.app.AppCompatActivity;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.R;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.json.JSONException;
 | 
				
			||||||
 | 
					import org.json.JSONObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.text.NumberFormat;
 | 
				
			||||||
 | 
					import java.text.SimpleDateFormat;
 | 
				
			||||||
 | 
					import java.util.Date;
 | 
				
			||||||
 | 
					import java.util.Locale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * ResultTransactionActivity - Enhanced Receipt-style Display using activity_receipt.xml
 | 
				
			||||||
 | 
					 * Shows EMV/Card transaction results using the same layout as QRIS receipts
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ResultTransactionActivity extends AppCompatActivity {
 | 
				
			||||||
 | 
					    private static final String TAG = "ResultTransaction";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ UI Components using activity_receipt.xml IDs
 | 
				
			||||||
 | 
					    private LinearLayout backNavigation;
 | 
				
			||||||
 | 
					    private ImageView backArrow;
 | 
				
			||||||
 | 
					    private TextView toolbarTitle;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Receipt details
 | 
				
			||||||
 | 
					    private TextView merchantName;
 | 
				
			||||||
 | 
					    private TextView merchantLocation;
 | 
				
			||||||
 | 
					    private TextView midText;
 | 
				
			||||||
 | 
					    private TextView tidText;
 | 
				
			||||||
 | 
					    private TextView transactionNumber;
 | 
				
			||||||
 | 
					    private TextView transactionDate;
 | 
				
			||||||
 | 
					    private TextView paymentMethod;
 | 
				
			||||||
 | 
					    private TextView cardType;
 | 
				
			||||||
 | 
					    private TextView transactionTotal;
 | 
				
			||||||
 | 
					    private TextView taxPercentage;
 | 
				
			||||||
 | 
					    private TextView serviceFee;
 | 
				
			||||||
 | 
					    private TextView finalTotal;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Action buttons
 | 
				
			||||||
 | 
					    private LinearLayout printButton;
 | 
				
			||||||
 | 
					    private LinearLayout emailButton;
 | 
				
			||||||
 | 
					    private Button finishButton;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Data from intent
 | 
				
			||||||
 | 
					    private String transactionAmount;
 | 
				
			||||||
 | 
					    private String cardTypeFromIntent;
 | 
				
			||||||
 | 
					    private boolean emvMode;
 | 
				
			||||||
 | 
					    private String referenceId;
 | 
				
			||||||
 | 
					    private String cardNo;
 | 
				
			||||||
 | 
					    private String midtransResponse;
 | 
				
			||||||
 | 
					    private boolean paymentSuccess;
 | 
				
			||||||
 | 
					    private String emvCardholderName;
 | 
				
			||||||
 | 
					    private String emvAid;
 | 
				
			||||||
 | 
					    private String emvExpiry;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Internal data
 | 
				
			||||||
 | 
					    private JSONObject responseJsonData;
 | 
				
			||||||
 | 
					    private boolean isNavigating = false;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Receipt calculation data
 | 
				
			||||||
 | 
					    private long subtotalAmount = 0;
 | 
				
			||||||
 | 
					    private long taxAmount = 0;
 | 
				
			||||||
 | 
					    private long serviceFeeAmount = 500; // Default service fee
 | 
				
			||||||
 | 
					    private double taxPercentageValue = 0.11; // 11% tax
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onCreate(Bundle savedInstanceState) {
 | 
				
			||||||
 | 
					        super.onCreate(savedInstanceState);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ CRITICAL: Use the same layout as ReceiptActivity
 | 
				
			||||||
 | 
					        setContentView(R.layout.activity_receipt);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "=== RESULT TRANSACTION ACTIVITY STARTED ===");
 | 
				
			||||||
 | 
					        Log.d(TAG, "✅ Using activity_receipt.xml layout");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        initViews();
 | 
				
			||||||
 | 
					        extractIntentData();
 | 
				
			||||||
 | 
					        debugAllDataSources();
 | 
				
			||||||
 | 
					        setupListeners();
 | 
				
			||||||
 | 
					        calculateAmounts();
 | 
				
			||||||
 | 
					        displayReceiptData();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        logTransactionDetails();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void initViews() {
 | 
				
			||||||
 | 
					        // ✅ Initialize views using activity_receipt.xml IDs
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Navigation
 | 
				
			||||||
 | 
					        backNavigation = findViewById(R.id.back_navigation);
 | 
				
			||||||
 | 
					        backArrow = findViewById(R.id.backArrow);
 | 
				
			||||||
 | 
					        toolbarTitle = findViewById(R.id.toolbarTitle);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Receipt details
 | 
				
			||||||
 | 
					        merchantName = findViewById(R.id.merchant_name);
 | 
				
			||||||
 | 
					        merchantLocation = findViewById(R.id.merchant_location);
 | 
				
			||||||
 | 
					        midText = findViewById(R.id.mid_text);
 | 
				
			||||||
 | 
					        tidText = findViewById(R.id.tid_text);
 | 
				
			||||||
 | 
					        transactionNumber = findViewById(R.id.transaction_number);
 | 
				
			||||||
 | 
					        transactionDate = findViewById(R.id.transaction_date);
 | 
				
			||||||
 | 
					        paymentMethod = findViewById(R.id.payment_method);
 | 
				
			||||||
 | 
					        cardType = findViewById(R.id.card_type);
 | 
				
			||||||
 | 
					        transactionTotal = findViewById(R.id.transaction_total);
 | 
				
			||||||
 | 
					        taxPercentage = findViewById(R.id.tax_percentage);
 | 
				
			||||||
 | 
					        serviceFee = findViewById(R.id.service_fee);
 | 
				
			||||||
 | 
					        finalTotal = findViewById(R.id.final_total);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Action buttons
 | 
				
			||||||
 | 
					        printButton = findViewById(R.id.print_button);
 | 
				
			||||||
 | 
					        emailButton = findViewById(R.id.email_button);
 | 
				
			||||||
 | 
					        finishButton = findViewById(R.id.finish_button);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "✅ All views initialized using activity_receipt.xml");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void extractIntentData() {
 | 
				
			||||||
 | 
					        Intent intent = getIntent();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        transactionAmount = intent.getStringExtra("TRANSACTION_AMOUNT");
 | 
				
			||||||
 | 
					        cardTypeFromIntent = intent.getStringExtra("CARD_TYPE");
 | 
				
			||||||
 | 
					        emvMode = intent.getBooleanExtra("EMV_MODE", false);
 | 
				
			||||||
 | 
					        referenceId = intent.getStringExtra("REFERENCE_ID");
 | 
				
			||||||
 | 
					        cardNo = intent.getStringExtra("CARD_NO");
 | 
				
			||||||
 | 
					        midtransResponse = intent.getStringExtra("MIDTRANS_RESPONSE");
 | 
				
			||||||
 | 
					        paymentSuccess = intent.getBooleanExtra("PAYMENT_SUCCESS", true);
 | 
				
			||||||
 | 
					        emvCardholderName = intent.getStringExtra("EMV_CARDHOLDER_NAME");
 | 
				
			||||||
 | 
					        emvAid = intent.getStringExtra("EMV_AID");
 | 
				
			||||||
 | 
					        emvExpiry = intent.getStringExtra("EMV_EXPIRY");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "=== EXTRACTING INTENT DATA ===");
 | 
				
			||||||
 | 
					        Log.d(TAG, "Card Type: " + cardTypeFromIntent);
 | 
				
			||||||
 | 
					        Log.d(TAG, "EMV Mode: " + emvMode);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Transaction Amount: " + transactionAmount);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Reference ID: " + referenceId);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Midtrans Response Length: " + (midtransResponse != null ? midtransResponse.length() : 0));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Parse Midtrans response if available
 | 
				
			||||||
 | 
					        if (midtransResponse != null && !midtransResponse.isEmpty()) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                responseJsonData = new JSONObject(midtransResponse);
 | 
				
			||||||
 | 
					                Log.d(TAG, "✅ Midtrans Response parsed successfully!");
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Check for bank field specifically
 | 
				
			||||||
 | 
					                if (responseJsonData.has("bank")) {
 | 
				
			||||||
 | 
					                    String bankValue = responseJsonData.getString("bank");
 | 
				
			||||||
 | 
					                    Log.d(TAG, "✅ Bank field found: '" + bankValue + "'");
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    Log.w(TAG, "⚠️ No 'bank' field in Midtrans response");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            } catch (JSONException e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "❌ Error parsing Midtrans response: " + e.getMessage());
 | 
				
			||||||
 | 
					                responseJsonData = null;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Log.w(TAG, "⚠️ No Midtrans response data available");
 | 
				
			||||||
 | 
					            responseJsonData = null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "===============================");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void setupListeners() {
 | 
				
			||||||
 | 
					        // Back navigation
 | 
				
			||||||
 | 
					        backNavigation.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            if (isNavigating) return;
 | 
				
			||||||
 | 
					            navigateBack();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        backArrow.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            if (isNavigating) return;
 | 
				
			||||||
 | 
					            navigateBack();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        toolbarTitle.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            if (isNavigating) return;
 | 
				
			||||||
 | 
					            navigateBack();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Print button
 | 
				
			||||||
 | 
					        printButton.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            showToast("Mencetak struk...");
 | 
				
			||||||
 | 
					            printReceipt();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Email button
 | 
				
			||||||
 | 
					        emailButton.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            showToast("Mengirim email...");
 | 
				
			||||||
 | 
					            emailReceipt();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ Finish button - Navigate to new transaction
 | 
				
			||||||
 | 
					        finishButton.setOnClickListener(v -> {
 | 
				
			||||||
 | 
					            if (isNavigating) return;
 | 
				
			||||||
 | 
					            navigateToNewTransaction();
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "✅ All click listeners setup");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void calculateAmounts() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (transactionAmount != null && !transactionAmount.isEmpty()) {
 | 
				
			||||||
 | 
					                subtotalAmount = Long.parseLong(transactionAmount);
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                subtotalAmount = 3500000; // Default amount for demo
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Calculate tax (11%)
 | 
				
			||||||
 | 
					            taxAmount = Math.round(subtotalAmount * taxPercentageValue);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Service fee is fixed
 | 
				
			||||||
 | 
					            serviceFeeAmount = 500;
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Log.d(TAG, "Amounts calculated - Subtotal: " + subtotalAmount + 
 | 
				
			||||||
 | 
					                      ", Tax: " + taxAmount + ", Service: " + serviceFeeAmount);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (NumberFormatException e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error calculating amounts: " + e.getMessage());
 | 
				
			||||||
 | 
					            // Set default values
 | 
				
			||||||
 | 
					            subtotalAmount = 3500000;
 | 
				
			||||||
 | 
					            taxAmount = 385000;
 | 
				
			||||||
 | 
					            serviceFeeAmount = 500;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void displayReceiptData() {
 | 
				
			||||||
 | 
					        Log.d(TAG, "=== DISPLAYING RECEIPT DATA ===");
 | 
				
			||||||
 | 
					        debugAllDataSources();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ 1. Set merchant data
 | 
				
			||||||
 | 
					        merchantName.setText("TOKO KLONTONG PAK EKO");
 | 
				
			||||||
 | 
					        merchantLocation.setText("Ciputat Baru, Tangsel");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ 2. Set MID and TID
 | 
				
			||||||
 | 
					        String tid = extractTidFromResponse();
 | 
				
			||||||
 | 
					        midText.setText("MID: " + tid);
 | 
				
			||||||
 | 
					        tidText.setText("TID: " + tid);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ 3. Set transaction number
 | 
				
			||||||
 | 
					        String displayTransactionNumber = extractTransactionNumberFromResponse();
 | 
				
			||||||
 | 
					        transactionNumber.setText(displayTransactionNumber);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ 4. Set transaction date
 | 
				
			||||||
 | 
					        String displayDate = formatTransactionDate();
 | 
				
			||||||
 | 
					        transactionDate.setText(displayDate);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ 5. Set payment method
 | 
				
			||||||
 | 
					        String displayPaymentMethod = getPaymentMethodDisplay();
 | 
				
			||||||
 | 
					        paymentMethod.setText(displayPaymentMethod);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ 6. ENHANCED: Set card type with comprehensive detection
 | 
				
			||||||
 | 
					        String displayCardType = getCardTypeDisplay();
 | 
				
			||||||
 | 
					        cardType.setText(displayCardType);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // ✅ 7. Set amount details
 | 
				
			||||||
 | 
					        NumberFormat formatter = NumberFormat.getNumberInstance(new Locale("id", "ID"));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        transactionTotal.setText(formatter.format(subtotalAmount));
 | 
				
			||||||
 | 
					        taxPercentage.setText(Math.round(taxPercentageValue * 100) + "%");
 | 
				
			||||||
 | 
					        serviceFee.setText(formatter.format(serviceFeeAmount));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Final total
 | 
				
			||||||
 | 
					        long finalTotalAmount = subtotalAmount + taxAmount + serviceFeeAmount;
 | 
				
			||||||
 | 
					        finalTotal.setText(formatter.format(finalTotalAmount));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "✅ Receipt data displayed successfully");
 | 
				
			||||||
 | 
					        Log.d(TAG, "   Payment Method: " + displayPaymentMethod);
 | 
				
			||||||
 | 
					        Log.d(TAG, "   Card Type: " + displayCardType);
 | 
				
			||||||
 | 
					        Log.d(TAG, "   Final Total: " + formatter.format(finalTotalAmount));
 | 
				
			||||||
 | 
					        Log.d(TAG, "================================");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String extractTidFromResponse() {
 | 
				
			||||||
 | 
					        if (responseJsonData != null && responseJsonData.has("tid")) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                return responseJsonData.getString("tid");
 | 
				
			||||||
 | 
					            } catch (JSONException e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "Error extracting TID: " + e.getMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return "123456789901"; // Default TID
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String extractTransactionNumberFromResponse() {
 | 
				
			||||||
 | 
					        if (responseJsonData != null) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                if (responseJsonData.has("transaction_id")) {
 | 
				
			||||||
 | 
					                    String fullTransactionId = responseJsonData.getString("transaction_id");
 | 
				
			||||||
 | 
					                    // Extract last 10 digits for display
 | 
				
			||||||
 | 
					                    if (fullTransactionId.length() > 10) {
 | 
				
			||||||
 | 
					                        return fullTransactionId.substring(fullTransactionId.length() - 10);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    return fullTransactionId;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (JSONException e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "Error extracting transaction number: " + e.getMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Generate from reference ID or use default
 | 
				
			||||||
 | 
					        if (referenceId != null && referenceId.length() > 10) {
 | 
				
			||||||
 | 
					            return referenceId.substring(referenceId.length() - 10);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return String.valueOf(System.currentTimeMillis() % 10000000000L);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String formatTransactionDate() {
 | 
				
			||||||
 | 
					        if (responseJsonData != null) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                if (responseJsonData.has("transaction_time")) {
 | 
				
			||||||
 | 
					                    String transactionTime = responseJsonData.getString("transaction_time");
 | 
				
			||||||
 | 
					                    return formatDateForDisplay(transactionTime);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (JSONException e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "Error extracting transaction time: " + e.getMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Use current date and time
 | 
				
			||||||
 | 
					        return formatDateForDisplay(new Date());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String formatDateForDisplay(String dateString) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            SimpleDateFormat inputFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault());
 | 
				
			||||||
 | 
					            SimpleDateFormat outputFormat = new SimpleDateFormat("dd MMMM yyyy HH:mm", new Locale("id", "ID"));
 | 
				
			||||||
 | 
					            Date date = inputFormat.parse(dateString);
 | 
				
			||||||
 | 
					            return outputFormat.format(date);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error formatting date: " + e.getMessage());
 | 
				
			||||||
 | 
					            return formatDateForDisplay(new Date());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String formatDateForDisplay(Date date) {
 | 
				
			||||||
 | 
					        SimpleDateFormat outputFormat = new SimpleDateFormat("dd MMMM yyyy HH:mm", new Locale("id", "ID"));
 | 
				
			||||||
 | 
					        return outputFormat.format(date);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String getPaymentMethodDisplay() {
 | 
				
			||||||
 | 
					        if (cardTypeFromIntent == null) return "Kartu Kredit";
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        switch (cardTypeFromIntent.toUpperCase()) {
 | 
				
			||||||
 | 
					            case "EMV_MIDTRANS":
 | 
				
			||||||
 | 
					            case "IC":
 | 
				
			||||||
 | 
					            case "NFC":
 | 
				
			||||||
 | 
					                return emvMode ? "Kartu Kredit (EMV)" : "Kartu Kredit";
 | 
				
			||||||
 | 
					            case "DEBIT":
 | 
				
			||||||
 | 
					                return emvMode ? "Kartu Debit (EMV)" : "Kartu Debit";
 | 
				
			||||||
 | 
					            case "MAGNETIC":
 | 
				
			||||||
 | 
					                return "Kartu Kredit";
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                return "Kartu Kredit";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ ENHANCED: Comprehensive bank detection for EMV transactions
 | 
				
			||||||
 | 
					    private String getCardTypeDisplay() {
 | 
				
			||||||
 | 
					        Log.d(TAG, "=== DETERMINING CARD TYPE DISPLAY (EMV) ===");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Priority 1: Get bank from Midtrans response (most accurate)
 | 
				
			||||||
 | 
					        if (responseJsonData != null) {
 | 
				
			||||||
 | 
					            Log.d(TAG, "✅ Midtrans response available, checking for bank...");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                String bankFromResponse = null;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (responseJsonData.has("bank")) {
 | 
				
			||||||
 | 
					                    bankFromResponse = responseJsonData.getString("bank");
 | 
				
			||||||
 | 
					                    Log.d(TAG, "Found 'bank' field: '" + bankFromResponse + "'");
 | 
				
			||||||
 | 
					                } else if (responseJsonData.has("issuer")) {
 | 
				
			||||||
 | 
					                    bankFromResponse = responseJsonData.getString("issuer");
 | 
				
			||||||
 | 
					                    Log.d(TAG, "Found 'issuer' field: '" + bankFromResponse + "'");
 | 
				
			||||||
 | 
					                } else if (responseJsonData.has("acquiring_bank")) {
 | 
				
			||||||
 | 
					                    bankFromResponse = responseJsonData.getString("acquiring_bank");
 | 
				
			||||||
 | 
					                    Log.d(TAG, "Found 'acquiring_bank' field: '" + bankFromResponse + "'");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (bankFromResponse != null && !bankFromResponse.trim().isEmpty()) {
 | 
				
			||||||
 | 
					                    String formattedBank = formatBankName(bankFromResponse);
 | 
				
			||||||
 | 
					                    Log.d(TAG, "✅ Bank from Midtrans response: '" + bankFromResponse + "' -> '" + formattedBank + "'");
 | 
				
			||||||
 | 
					                    return formattedBank;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            } catch (JSONException e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "❌ Error extracting bank from response: " + e.getMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Priority 2: EMV AID detection
 | 
				
			||||||
 | 
					        Log.d(TAG, "Trying EMV AID detection...");
 | 
				
			||||||
 | 
					        if (emvAid != null && !emvAid.trim().isEmpty()) {
 | 
				
			||||||
 | 
					            String bankFromAid = getBankFromAid(emvAid);
 | 
				
			||||||
 | 
					            if (!bankFromAid.equals("BCA")) { // If not default
 | 
				
			||||||
 | 
					                Log.d(TAG, "✅ Bank from EMV AID: " + bankFromAid);
 | 
				
			||||||
 | 
					                return bankFromAid;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Priority 3: Card BIN detection
 | 
				
			||||||
 | 
					        Log.d(TAG, "Trying Card BIN detection...");
 | 
				
			||||||
 | 
					        if (cardNo != null && cardNo.length() >= 6) {
 | 
				
			||||||
 | 
					            String cardBin = cardNo.substring(0, 6);
 | 
				
			||||||
 | 
					            String bankFromBin = getBankFromComprehensiveBin(cardBin);
 | 
				
			||||||
 | 
					            Log.d(TAG, "✅ Bank from BIN (" + cardBin + "): " + bankFromBin);
 | 
				
			||||||
 | 
					            return bankFromBin;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "⚠️ Using default bank: BCA");
 | 
				
			||||||
 | 
					        Log.d(TAG, "====================================");
 | 
				
			||||||
 | 
					        return "BCA"; // Default fallback
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ ENHANCED: Better bank name formatting
 | 
				
			||||||
 | 
					    private String formatBankName(String bankName) {
 | 
				
			||||||
 | 
					        if (bankName == null || bankName.trim().isEmpty()) {
 | 
				
			||||||
 | 
					            return "BCA"; // Default
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        String formatted = bankName.trim().toUpperCase();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Handle common bank name variations
 | 
				
			||||||
 | 
					        switch (formatted) {
 | 
				
			||||||
 | 
					            case "BCA":
 | 
				
			||||||
 | 
					            case "BANK BCA":
 | 
				
			||||||
 | 
					            case "BANK CENTRAL ASIA":
 | 
				
			||||||
 | 
					                return "BCA";
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            case "MANDIRI":
 | 
				
			||||||
 | 
					            case "BANK MANDIRI":
 | 
				
			||||||
 | 
					                return "Mandiri";
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            case "BNI":
 | 
				
			||||||
 | 
					            case "BANK BNI":
 | 
				
			||||||
 | 
					            case "BANK NEGARA INDONESIA":
 | 
				
			||||||
 | 
					                return "BNI";
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            case "BRI":
 | 
				
			||||||
 | 
					            case "BANK BRI":
 | 
				
			||||||
 | 
					            case "BANK RAKYAT INDONESIA":
 | 
				
			||||||
 | 
					                return "BRI";
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            case "CIMB":
 | 
				
			||||||
 | 
					            case "CIMB NIAGA":
 | 
				
			||||||
 | 
					            case "BANK CIMB NIAGA":
 | 
				
			||||||
 | 
					                return "CIMB Niaga";
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            case "DANAMON":
 | 
				
			||||||
 | 
					            case "BANK DANAMON":
 | 
				
			||||||
 | 
					                return "Danamon";
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            case "PERMATA":
 | 
				
			||||||
 | 
					            case "BANK PERMATA":
 | 
				
			||||||
 | 
					                return "Permata";
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                return capitalizeFirstLetter(bankName);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String getBankFromAid(String aid) {
 | 
				
			||||||
 | 
					        // AID to Indonesian bank mapping
 | 
				
			||||||
 | 
					        if (aid.contains("A0000000031010")) {
 | 
				
			||||||
 | 
					            // VISA - check if we have card number for better detection
 | 
				
			||||||
 | 
					            if (cardNo != null && cardNo.length() >= 6) {
 | 
				
			||||||
 | 
					                return getBankFromComprehensiveBin(cardNo.substring(0, 6));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return "BCA"; // Default for VISA
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (aid.contains("A0000000041010")) {
 | 
				
			||||||
 | 
					            // MASTERCARD
 | 
				
			||||||
 | 
					            if (cardNo != null && cardNo.length() >= 6) {
 | 
				
			||||||
 | 
					                return getBankFromComprehensiveBin(cardNo.substring(0, 6));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return "Mandiri"; // Default for Mastercard
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return "BCA"; // Ultimate fallback
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ ENHANCED: Comprehensive Indonesian bank BIN mapping
 | 
				
			||||||
 | 
					    private String getBankFromComprehensiveBin(String bin) {
 | 
				
			||||||
 | 
					        if (bin == null || bin.length() < 4) {
 | 
				
			||||||
 | 
					            return "BCA"; // Default
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        String bin4 = bin.substring(0, 4);
 | 
				
			||||||
 | 
					        String bin6 = bin.length() >= 6 ? bin.substring(0, 6) : bin4;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // BCA patterns
 | 
				
			||||||
 | 
					        if (bin4.equals("4621") || bin4.equals("4699") || bin4.equals("5221") || bin4.equals("6277")) {
 | 
				
			||||||
 | 
					            return "BCA";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // MANDIRI patterns
 | 
				
			||||||
 | 
					        if (bin4.equals("4313") || bin4.equals("5573") || bin4.equals("6011") || bin4.equals("6234")) {
 | 
				
			||||||
 | 
					            return "Mandiri";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // BNI patterns  
 | 
				
			||||||
 | 
					        if (bin4.equals("4603") || bin4.equals("1946") || bin4.equals("5264")) {
 | 
				
			||||||
 | 
					            return "BNI";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // BRI patterns
 | 
				
			||||||
 | 
					        if (bin4.equals("4578") || bin4.equals("4479") || bin4.equals("5208")) {
 | 
				
			||||||
 | 
					            return "BRI";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // CIMB NIAGA patterns
 | 
				
			||||||
 | 
					        if (bin4.equals("4599") || bin4.equals("5249")) {
 | 
				
			||||||
 | 
					            return "CIMB Niaga";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // DANAMON patterns
 | 
				
			||||||
 | 
					        if (bin4.equals("4055") || bin4.equals("5108")) {
 | 
				
			||||||
 | 
					            return "Danamon";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Default fallback
 | 
				
			||||||
 | 
					        Log.d(TAG, "Unknown BIN pattern: " + bin6 + ", using default BCA");
 | 
				
			||||||
 | 
					        return "BCA";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String capitalizeFirstLetter(String input) {
 | 
				
			||||||
 | 
					        if (input == null || input.isEmpty()) {
 | 
				
			||||||
 | 
					            return input;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return input.substring(0, 1).toUpperCase() + input.substring(1).toLowerCase();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ Debug methods
 | 
				
			||||||
 | 
					    private void debugAllDataSources() {
 | 
				
			||||||
 | 
					        Log.d(TAG, "=== DEBUGGING ALL DATA SOURCES ===");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (responseJsonData != null) {
 | 
				
			||||||
 | 
					            Log.d(TAG, "Midtrans Response Available:");
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                java.util.Iterator<String> keys = responseJsonData.keys();
 | 
				
			||||||
 | 
					                while (keys.hasNext()) {
 | 
				
			||||||
 | 
					                    String key = keys.next();
 | 
				
			||||||
 | 
					                    Object value = responseJsonData.get(key);
 | 
				
			||||||
 | 
					                    Log.d(TAG, "  " + key + ": " + value);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "Error iterating response: " + e.getMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Log.d(TAG, "❌ No Midtrans Response Data");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "EMV Data:");
 | 
				
			||||||
 | 
					        Log.d(TAG, "  Card Number: " + (cardNo != null ? maskCardNumber(cardNo) : "null"));
 | 
				
			||||||
 | 
					        Log.d(TAG, "  EMV AID: " + emvAid);
 | 
				
			||||||
 | 
					        Log.d(TAG, "  EMV Cardholder: " + emvCardholderName);
 | 
				
			||||||
 | 
					        Log.d(TAG, "  EMV Mode: " + emvMode);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "==================================");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void logTransactionDetails() {
 | 
				
			||||||
 | 
					        Log.d(TAG, "=== RECEIPT DETAILS ===");
 | 
				
			||||||
 | 
					        Log.d(TAG, "Reference ID: " + referenceId);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Card Number: " + (cardNo != null ? maskCardNumber(cardNo) : "N/A"));
 | 
				
			||||||
 | 
					        Log.d(TAG, "Subtotal: " + subtotalAmount);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Tax: " + taxAmount);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Service Fee: " + serviceFeeAmount);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Final Total: " + (subtotalAmount + taxAmount + serviceFeeAmount));
 | 
				
			||||||
 | 
					        Log.d(TAG, "======================");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Action Methods
 | 
				
			||||||
 | 
					    private void printReceipt() {
 | 
				
			||||||
 | 
					        Log.d(TAG, "Print receipt requested");
 | 
				
			||||||
 | 
					        showToast("Fitur cetak akan segera tersedia");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void emailReceipt() {
 | 
				
			||||||
 | 
					        Log.d(TAG, "Email receipt requested");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Intent emailIntent = new Intent(Intent.ACTION_SEND);
 | 
				
			||||||
 | 
					        emailIntent.setType("text/plain");
 | 
				
			||||||
 | 
					        emailIntent.putExtra(Intent.EXTRA_SUBJECT, "Struk Pembayaran - " + extractTransactionNumberFromResponse());
 | 
				
			||||||
 | 
					        emailIntent.putExtra(Intent.EXTRA_TEXT, generateEmailContent());
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            startActivity(Intent.createChooser(emailIntent, "Kirim Email"));
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error sending email: " + e.getMessage());
 | 
				
			||||||
 | 
					            showToast("Tidak dapat mengirim email");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private String generateEmailContent() {
 | 
				
			||||||
 | 
					        StringBuilder content = new StringBuilder();
 | 
				
			||||||
 | 
					        NumberFormat formatter = NumberFormat.getNumberInstance(new Locale("id", "ID"));
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        content.append("STRUK PEMBAYARAN EMV/CARD\n");
 | 
				
			||||||
 | 
					        content.append("==========================\n\n");
 | 
				
			||||||
 | 
					        content.append("TOKO KLONTONG PAK EKO\n");
 | 
				
			||||||
 | 
					        content.append("Ciputat Baru, Tangsel\n\n");
 | 
				
			||||||
 | 
					        content.append("TID: ").append(extractTidFromResponse()).append("\n");
 | 
				
			||||||
 | 
					        content.append("Nomor Transaksi: ").append(extractTransactionNumberFromResponse()).append("\n");
 | 
				
			||||||
 | 
					        content.append("Tanggal: ").append(formatTransactionDate()).append("\n");
 | 
				
			||||||
 | 
					        content.append("Metode: ").append(getPaymentMethodDisplay()).append("\n");
 | 
				
			||||||
 | 
					        content.append("Jenis Kartu: ").append(getCardTypeDisplay()).append("\n\n");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (emvMode && emvCardholderName != null) {
 | 
				
			||||||
 | 
					            content.append("DETAIL EMV:\n");
 | 
				
			||||||
 | 
					            content.append("Cardholder: ").append(emvCardholderName).append("\n");
 | 
				
			||||||
 | 
					            content.append("AID: ").append(emvAid).append("\n\n");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        content.append("RINCIAN PEMBAYARAN:\n");
 | 
				
			||||||
 | 
					        content.append("Total Transaksi: Rp ").append(formatter.format(subtotalAmount)).append("\n");
 | 
				
			||||||
 | 
					        content.append("Pajak (11%): Rp ").append(formatter.format(taxAmount)).append("\n");
 | 
				
			||||||
 | 
					        content.append("Biaya Layanan: Rp ").append(formatter.format(serviceFeeAmount)).append("\n");
 | 
				
			||||||
 | 
					        content.append("------------------------\n");
 | 
				
			||||||
 | 
					        content.append("TOTAL: Rp ").append(formatter.format(subtotalAmount + taxAmount + serviceFeeAmount)).append("\n");
 | 
				
			||||||
 | 
					        content.append("\nTerima kasih atas pembayaran Anda!");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        return content.toString();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Navigation Methods
 | 
				
			||||||
 | 
					    private void navigateBack() {
 | 
				
			||||||
 | 
					        if (isNavigating) return;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "Navigating back");
 | 
				
			||||||
 | 
					        isNavigating = true;
 | 
				
			||||||
 | 
					        finish();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void navigateToNewTransaction() {
 | 
				
			||||||
 | 
					        if (isNavigating) return;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        new AlertDialog.Builder(this)
 | 
				
			||||||
 | 
					            .setTitle("Transaksi Baru")
 | 
				
			||||||
 | 
					            .setMessage("Apakah Anda ingin melakukan transaksi baru?")
 | 
				
			||||||
 | 
					            .setPositiveButton("Ya", (dialog, which) -> {
 | 
				
			||||||
 | 
					                performNavigateToNewTransaction();
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            .setNegativeButton("Tidak", null)
 | 
				
			||||||
 | 
					            .show();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void performNavigateToNewTransaction() {
 | 
				
			||||||
 | 
					        Log.d(TAG, "=== NAVIGATING TO NEW TRANSACTION ===");
 | 
				
			||||||
 | 
					        isNavigating = true;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                Intent intent = new Intent(this, CreateTransactionActivity.class);
 | 
				
			||||||
 | 
					                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
 | 
				
			||||||
 | 
					                startActivity(intent);
 | 
				
			||||||
 | 
					                finish();
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "Error navigating to new transaction: " + e.getMessage());
 | 
				
			||||||
 | 
					                isNavigating = false;
 | 
				
			||||||
 | 
					                showToast("Gagal membuka transaksi baru");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }, 300);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Helper Methods
 | 
				
			||||||
 | 
					    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);
 | 
				
			||||||
 | 
					        return first4 + "****" + last4;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void showToast(String message) {
 | 
				
			||||||
 | 
					        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onBackPressed() {
 | 
				
			||||||
 | 
					        if (isNavigating) {
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        navigateBack();
 | 
				
			||||||
 | 
					        super.onBackPressed();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    protected void onDestroy() {
 | 
				
			||||||
 | 
					        Log.d(TAG, "ResultTransactionActivity destroyed");
 | 
				
			||||||
 | 
					        super.onDestroy();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,258 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.transaction.managers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.os.Handler;
 | 
				
			||||||
 | 
					import android.os.Looper;
 | 
				
			||||||
 | 
					import android.os.RemoteException;
 | 
				
			||||||
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.MyApplication;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidl.AidlConstants.CardType;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.readcard.CheckCardCallbackV2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * CardScannerManager - Handles card detection for both EMV and Simple modes
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class CardScannerManager {
 | 
				
			||||||
 | 
					    private static final String TAG = "CardScannerManager";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private CardScannerCallback callback;
 | 
				
			||||||
 | 
					    private boolean isProcessing = false;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public interface CardScannerCallback {
 | 
				
			||||||
 | 
					        void onCardDetected(String cardType, Bundle cardData);
 | 
				
			||||||
 | 
					        void onEMVCardDetected(int cardType);
 | 
				
			||||||
 | 
					        void onScanError(String errorMessage);
 | 
				
			||||||
 | 
					        void onScanProgress(String message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public CardScannerManager(CardScannerCallback callback) {
 | 
				
			||||||
 | 
					        this.callback = callback;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void startScanning(boolean isEMVMode) {
 | 
				
			||||||
 | 
					        if (isProcessing) {
 | 
				
			||||||
 | 
					            Log.d(TAG, "Card check already in progress - ignoring call");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "Starting card check - setting isProcessing = true");
 | 
				
			||||||
 | 
					        isProcessing = true;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Small delay to ensure everything is ready
 | 
				
			||||||
 | 
					            new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
				
			||||||
 | 
					                if (isProcessing) {
 | 
				
			||||||
 | 
					                    if (isEMVMode) {
 | 
				
			||||||
 | 
					                        startEMVCardCheck();
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        startSimpleCardCheck();
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }, 500);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error in startScanning: " + e.getMessage(), e);
 | 
				
			||||||
 | 
					            handleScanError("Error: " + e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void stopScanning() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (MyApplication.app != null && MyApplication.app.readCardOptV2 != null) {
 | 
				
			||||||
 | 
					                MyApplication.app.readCardOptV2.cancelCheckCard();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            Log.d(TAG, "Card scanning stopped");
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error stopping card check: " + e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public boolean isScanning() {
 | 
				
			||||||
 | 
					        return isProcessing;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void startEMVCardCheck() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onScanProgress("EMV Mode: Starting card scan...");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            int cardType = AidlConstantsV2.CardType.NFC.getValue() | AidlConstantsV2.CardType.IC.getValue();
 | 
				
			||||||
 | 
					            Log.d(TAG, "Starting EMV checkCard with cardType: " + cardType);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            MyApplication.app.readCardOptV2.checkCard(cardType, mEMVCheckCardCallback, 60);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error in startEMVCardCheck: " + e.getMessage());
 | 
				
			||||||
 | 
					            handleScanError("Error starting EMV card scan: " + e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void startSimpleCardCheck() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (!MyApplication.app.isConnectPaySDK()) {
 | 
				
			||||||
 | 
					                if (callback != null) {
 | 
				
			||||||
 | 
					                    callback.onScanProgress("Connecting to PaySDK...");
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                MyApplication.app.bindPaySDKService();
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onScanProgress("Simple Mode: Starting card scan...");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            int cardType = CardType.MAGNETIC.getValue() | CardType.IC.getValue() | CardType.NFC.getValue();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            MyApplication.app.readCardOptV2.checkCard(cardType, mSimpleCheckCardCallback, 60);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					            handleScanError("Error starting card scan: " + e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void handleScanError(String errorMessage) {
 | 
				
			||||||
 | 
					        Log.e(TAG, "Scan error: " + errorMessage);
 | 
				
			||||||
 | 
					        isProcessing = false;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (callback != null) {
 | 
				
			||||||
 | 
					            callback.onScanError(errorMessage);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void resetScanning() {
 | 
				
			||||||
 | 
					        Log.d(TAG, "Resetting scanning state");
 | 
				
			||||||
 | 
					        isProcessing = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Simple Card Detection Callback
 | 
				
			||||||
 | 
					    private final CheckCardCallbackV2 mSimpleCheckCardCallback = new CheckCardCallbackV2.Stub() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void findMagCard(Bundle info) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "Simple Mode: findMagCard callback triggered");
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onCardDetected("MAGNETIC", info);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void findICCard(String atr) throws RemoteException {
 | 
				
			||||||
 | 
					            Bundle info = new Bundle();
 | 
				
			||||||
 | 
					            info.putString("atr", atr);
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onCardDetected("IC", info);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void findRFCard(String uuid) throws RemoteException {
 | 
				
			||||||
 | 
					            Bundle info = new Bundle();
 | 
				
			||||||
 | 
					            info.putString("uuid", uuid);
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onCardDetected("NFC", info);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onError(int code, String message) throws RemoteException {
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onScanError("Card error: " + message);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void findICCardEx(Bundle info) throws RemoteException {
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onCardDetected("IC", info);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void findRFCardEx(Bundle info) throws RemoteException {
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onCardDetected("NFC", info);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onErrorEx(Bundle info) throws RemoteException {
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            String msg = info.getString("message", "Unknown error");
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onScanError("Card error: " + msg);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // EMV Card Detection Callback
 | 
				
			||||||
 | 
					    private final CheckCardCallbackV2 mEMVCheckCardCallback = new CheckCardCallbackV2.Stub() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void findMagCard(Bundle info) throws RemoteException {
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onCardDetected("MAGNETIC", info);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void findICCard(String atr) throws RemoteException {
 | 
				
			||||||
 | 
					            MyApplication.app.basicOptV2.buzzerOnDevice(1, 2750, 200, 0);
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onEMVCardDetected(AidlConstantsV2.CardType.IC.getValue());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void findRFCard(String uuid) throws RemoteException {
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onEMVCardDetected(AidlConstantsV2.CardType.NFC.getValue());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onError(int code, String message) throws RemoteException {
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onScanError("EMV Error: " + message);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void findICCardEx(Bundle info) throws RemoteException {
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onEMVCardDetected(AidlConstantsV2.CardType.IC.getValue());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void findRFCardEx(Bundle info) throws RemoteException {
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onEMVCardDetected(AidlConstantsV2.CardType.NFC.getValue());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onErrorEx(Bundle info) throws RemoteException {
 | 
				
			||||||
 | 
					            isProcessing = false;
 | 
				
			||||||
 | 
					            String msg = info.getString("message", "Unknown error");
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onScanError("EMV Error: " + msg);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,417 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.transaction.managers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.os.Handler;
 | 
				
			||||||
 | 
					import android.os.Looper;
 | 
				
			||||||
 | 
					import android.os.RemoteException;
 | 
				
			||||||
 | 
					import android.text.TextUtils;
 | 
				
			||||||
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.MyApplication;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.AidlConstantsV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.bean.EMVCandidateV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.emv.EMVListenerV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.emv.EMVOptV2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * EMVManager - Handles all EMV related operations
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class EMVManager {
 | 
				
			||||||
 | 
					    private static final String TAG = "EMVManager";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private EMVOptV2 mEMVOptV2;
 | 
				
			||||||
 | 
					    private EMVManagerCallback callback;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // EMV Process Variables
 | 
				
			||||||
 | 
					    private int mCardType;
 | 
				
			||||||
 | 
					    private String mCardNo;
 | 
				
			||||||
 | 
					    private int mPinType;
 | 
				
			||||||
 | 
					    private String mCertInfo;
 | 
				
			||||||
 | 
					    private int mProcessStep;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public interface EMVManagerCallback {
 | 
				
			||||||
 | 
					        void onAppSelect(String[] candidateNames);
 | 
				
			||||||
 | 
					        void onFinalAppSelect();
 | 
				
			||||||
 | 
					        void onConfirmCardNo(String cardNo);
 | 
				
			||||||
 | 
					        void onCertVerify(String certInfo);
 | 
				
			||||||
 | 
					        void onShowPinPad(int pinType);
 | 
				
			||||||
 | 
					        void onOnlineProcess();
 | 
				
			||||||
 | 
					        void onSignature();
 | 
				
			||||||
 | 
					        void onTransactionSuccess(int code, String desc);
 | 
				
			||||||
 | 
					        void onTransactionFailed(int code, String desc);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public EMVManager(EMVManagerCallback callback) {
 | 
				
			||||||
 | 
					        this.callback = callback;
 | 
				
			||||||
 | 
					        initEMVComponents();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void initEMVComponents() {
 | 
				
			||||||
 | 
					        if (MyApplication.app != null) {
 | 
				
			||||||
 | 
					            mEMVOptV2 = MyApplication.app.emvOptV2;
 | 
				
			||||||
 | 
					            Log.d(TAG, "EMV components initialized");
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Log.e(TAG, "MyApplication.app is null");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void initEMVData() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (mEMVOptV2 != null) {
 | 
				
			||||||
 | 
					                mEMVOptV2.initEmvProcess();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
				
			||||||
 | 
					                    try {
 | 
				
			||||||
 | 
					                        initEmvTlvData();
 | 
				
			||||||
 | 
					                        Log.d(TAG, "EMV data initialized successfully");
 | 
				
			||||||
 | 
					                    } catch (Exception e) {
 | 
				
			||||||
 | 
					                        Log.e(TAG, "Error in delayed EMV init: " + e.getMessage());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }, 500);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error initializing EMV data: " + e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void initEmvTlvData() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Set PayPass (MasterCard) TLV data
 | 
				
			||||||
 | 
					            String[] tagsPayPass = {"DF8117", "DF8118", "DF8119", "DF811F", "DF811E", "DF812C",
 | 
				
			||||||
 | 
					                    "DF8123", "DF8124", "DF8125", "DF8126", "DF811B", "DF811D", "DF8122", "DF8120", "DF8121"};
 | 
				
			||||||
 | 
					            String[] valuesPayPass = {"E0", "F8", "F8", "E8", "00", "00",
 | 
				
			||||||
 | 
					                    "000000000000", "000000100000", "999999999999", "000000100000",
 | 
				
			||||||
 | 
					                    "30", "02", "0000000000", "000000000000", "000000000000"};
 | 
				
			||||||
 | 
					            mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PAYPASS, tagsPayPass, valuesPayPass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Set AMEX TLV data
 | 
				
			||||||
 | 
					            String[] tagsAE = {"9F6D", "9F6E", "9F33", "9F35", "DF8168", "DF8167", "DF8169", "DF8170"};
 | 
				
			||||||
 | 
					            String[] valuesAE = {"C0", "D8E00000", "E0E888", "22", "00", "00", "00", "60"};
 | 
				
			||||||
 | 
					            mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_AE, tagsAE, valuesAE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Set JCB TLV data
 | 
				
			||||||
 | 
					            String[] tagsJCB = {"9F53", "DF8161"};
 | 
				
			||||||
 | 
					            String[] valuesJCB = {"708000", "7F00"};
 | 
				
			||||||
 | 
					            mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_JCB, tagsJCB, valuesJCB);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Set DPAS TLV data
 | 
				
			||||||
 | 
					            String[] tagsDPAS = {"9F66"};
 | 
				
			||||||
 | 
					            String[] valuesDPAS = {"B600C000"};
 | 
				
			||||||
 | 
					            mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_DPAS, tagsDPAS, valuesDPAS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Set Flash TLV data
 | 
				
			||||||
 | 
					            String[] tagsFLASH = {"9F58", "9F59", "9F5A", "9F5D", "9F5E"};
 | 
				
			||||||
 | 
					            String[] valuesFLASH = {"03", "D88700", "00", "000000000000", "E000"};
 | 
				
			||||||
 | 
					            mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_FLASH, tagsFLASH, valuesFLASH);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Set Pure TLV data
 | 
				
			||||||
 | 
					            String[] tagsPURE = {"DF7F", "DF8134", "DF8133"};
 | 
				
			||||||
 | 
					            String[] valuesPURE = {"A0000007271010", "DF", "36006043F9"};
 | 
				
			||||||
 | 
					            mEMVOptV2.setTlvList(AidlConstantsV2.EMV.TLVOpCode.OP_PURE, tagsPURE, valuesPURE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Bundle bundle = new Bundle();
 | 
				
			||||||
 | 
					            bundle.putBoolean("optOnlineRes", true);
 | 
				
			||||||
 | 
					            mEMVOptV2.setTermParamEx(bundle);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (RemoteException e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error setting EMV TLV data: " + e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void startEMVTransaction(String transactionAmount, int cardType) {
 | 
				
			||||||
 | 
					        if (mProcessStep != 0) {
 | 
				
			||||||
 | 
					            Log.d(TAG, "EMV transaction already in progress (step: " + mProcessStep + ") - ignoring call");
 | 
				
			||||||
 | 
					            return;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "Starting EMV transaction process");
 | 
				
			||||||
 | 
					        mProcessStep = 1;
 | 
				
			||||||
 | 
					        mCardType = cardType;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            mEMVOptV2.initEmvProcess();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            new Handler(Looper.getMainLooper()).postDelayed(() -> {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    if (mProcessStep <= 0) {
 | 
				
			||||||
 | 
					                        Log.d(TAG, "EMV process was cancelled - not starting");
 | 
				
			||||||
 | 
					                        return;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    Bundle bundle = new Bundle();
 | 
				
			||||||
 | 
					                    bundle.putString("amount", transactionAmount);
 | 
				
			||||||
 | 
					                    bundle.putString("transType", "00");
 | 
				
			||||||
 | 
					                    bundle.putInt("flowType", AidlConstantsV2.EMV.FlowType.TYPE_EMV_STANDARD);
 | 
				
			||||||
 | 
					                    bundle.putInt("cardType", mCardType);
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    Log.d(TAG, "Starting transactProcessEx with reset EMV");
 | 
				
			||||||
 | 
					                    mEMVOptV2.transactProcessEx(bundle, mEMVListener);
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                } catch (Exception e) {
 | 
				
			||||||
 | 
					                    Log.e(TAG, "Error in delayed EMV start: " + e.getMessage(), e);
 | 
				
			||||||
 | 
					                    if (callback != null) {
 | 
				
			||||||
 | 
					                        callback.onTransactionFailed(-1, "Error starting EMV: " + e.getMessage());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }, 300);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error starting EMV transaction: " + e.getMessage());
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onTransactionFailed(-1, "Error starting EMV transaction: " + e.getMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void resetEMVProcess() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (mEMVOptV2 != null) {
 | 
				
			||||||
 | 
					                mEMVOptV2.initEmvProcess();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            mProcessStep = 0;
 | 
				
			||||||
 | 
					            mCardNo = null;
 | 
				
			||||||
 | 
					            Log.d(TAG, "EMV process reset");
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error resetting EMV process: " + e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // EMV Import Methods
 | 
				
			||||||
 | 
					    public void importAppSelect(int selectIndex) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            mEMVOptV2.importAppSelect(selectIndex);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void importFinalAppSelectStatus(int status) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            mEMVOptV2.importAppFinalSelectStatus(status);
 | 
				
			||||||
 | 
					        } catch (RemoteException e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void importCardNoStatus(int status) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            mEMVOptV2.importCardNoStatus(status);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void importCertStatus(int status) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            mEMVOptV2.importCertStatus(status);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void importPinInputStatus(int inputResult) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            mEMVOptV2.importPinInputStatus(mPinType, inputResult);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public void importSignatureStatus(int status) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            mEMVOptV2.importSignatureStatus(status);
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            e.printStackTrace();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void mockOnlineProcess() {
 | 
				
			||||||
 | 
					        new Thread(() -> {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                Thread.sleep(2000);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    String[] tags = {"71", "72", "91", "8A", "89"};
 | 
				
			||||||
 | 
					                    String[] values = {"", "", "", "", ""};
 | 
				
			||||||
 | 
					                    byte[] out = new byte[1024];
 | 
				
			||||||
 | 
					                    int len = mEMVOptV2.importOnlineProcStatus(0, tags, values, out);
 | 
				
			||||||
 | 
					                    if (len < 0) {
 | 
				
			||||||
 | 
					                        Log.e(TAG, "importOnlineProcessStatus error,code:" + len);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } catch (Exception e) {
 | 
				
			||||||
 | 
					                    e.printStackTrace();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                e.printStackTrace();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }).start();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Getters
 | 
				
			||||||
 | 
					    public String getCardNo() {
 | 
				
			||||||
 | 
					        return mCardNo;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public int getCardType() {
 | 
				
			||||||
 | 
					        return mCardType;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public int getPinType() {
 | 
				
			||||||
 | 
					        return mPinType;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Helper Methods
 | 
				
			||||||
 | 
					    public String maskCardNumber(String cardNo) {
 | 
				
			||||||
 | 
					        if (cardNo == null || cardNo.length() < 8) {
 | 
				
			||||||
 | 
					            return cardNo;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        String first4 = cardNo.substring(0, 4);
 | 
				
			||||||
 | 
					        String last4 = cardNo.substring(cardNo.length() - 4);
 | 
				
			||||||
 | 
					        StringBuilder middle = new StringBuilder();
 | 
				
			||||||
 | 
					        for (int i = 0; i < cardNo.length() - 8; i++) {
 | 
				
			||||||
 | 
					            middle.append("*");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return first4 + middle.toString() + last4;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public String[] getCandidateNames(List<EMVCandidateV2> candiList) {
 | 
				
			||||||
 | 
					        if (candiList == null || candiList.size() == 0) return new String[0];
 | 
				
			||||||
 | 
					        String[] result = new String[candiList.size()];
 | 
				
			||||||
 | 
					        for (int i = 0; i < candiList.size(); i++) {
 | 
				
			||||||
 | 
					            EMVCandidateV2 candi = candiList.get(i);
 | 
				
			||||||
 | 
					            String name = candi.appPreName;
 | 
				
			||||||
 | 
					            name = TextUtils.isEmpty(name) ? candi.appLabel : name;
 | 
				
			||||||
 | 
					            name = TextUtils.isEmpty(name) ? candi.appName : name;
 | 
				
			||||||
 | 
					            name = TextUtils.isEmpty(name) ? "" : name;
 | 
				
			||||||
 | 
					            result[i] = name;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // EMV Listener
 | 
				
			||||||
 | 
					    private final EMVListenerV2 mEMVListener = new EMVListenerV2.Stub() {
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onWaitAppSelect(List<EMVCandidateV2> appNameList, boolean isFirstSelect) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onWaitAppSelect isFirstSelect:" + isFirstSelect);
 | 
				
			||||||
 | 
					            mProcessStep = 1; // EMV_APP_SELECT
 | 
				
			||||||
 | 
					            String[] candidateNames = getCandidateNames(appNameList);
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onAppSelect(candidateNames);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onAppFinalSelect(String tag9F06Value) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onAppFinalSelect tag9F06Value:" + tag9F06Value);
 | 
				
			||||||
 | 
					            mProcessStep = 2; // EMV_FINAL_APP_SELECT
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onFinalAppSelect();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onConfirmCardNo(String cardNo) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onConfirmCardNo cardNo:" + maskCardNumber(cardNo));
 | 
				
			||||||
 | 
					            mCardNo = cardNo;
 | 
				
			||||||
 | 
					            mProcessStep = 3; // EMV_CONFIRM_CARD_NO
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onConfirmCardNo(cardNo);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onRequestShowPinPad(int pinType, int remainTime) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onRequestShowPinPad pinType:" + pinType + " remainTime:" + remainTime);
 | 
				
			||||||
 | 
					            mPinType = pinType;
 | 
				
			||||||
 | 
					            mProcessStep = 5; // EMV_SHOW_PIN_PAD
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onShowPinPad(pinType);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onRequestSignature() throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onRequestSignature");
 | 
				
			||||||
 | 
					            mProcessStep = 7; // EMV_SIGNATURE
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onSignature();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onCertVerify(int certType, String certInfo) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onCertVerify certType:" + certType + " certInfo:" + certInfo);
 | 
				
			||||||
 | 
					            mCertInfo = certInfo;
 | 
				
			||||||
 | 
					            mProcessStep = 4; // EMV_CERT_VERIFY
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onCertVerify(certInfo);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onOnlineProc() throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onOnlineProcess");
 | 
				
			||||||
 | 
					            mProcessStep = 6; // EMV_ONLINE_PROCESS
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onOnlineProcess();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onCardDataExchangeComplete() throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onCardDataExchangeComplete");
 | 
				
			||||||
 | 
					            if (mCardType == AidlConstantsV2.CardType.NFC.getValue()) {
 | 
				
			||||||
 | 
					                MyApplication.app.basicOptV2.buzzerOnDevice(1, 2750, 200, 0);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onTransResult(int code, String desc) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onTransResult code:" + code + " desc:" + desc);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (code == 1 || code == 2 || code == 5 || code == 6) {
 | 
				
			||||||
 | 
					                if (callback != null) {
 | 
				
			||||||
 | 
					                    callback.onTransactionSuccess(code, desc);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                if (callback != null) {
 | 
				
			||||||
 | 
					                    callback.onTransactionFailed(code, desc);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onConfirmationCodeVerified() throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onConfirmationCodeVerified");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onRequestDataExchange(String cardNo) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onRequestDataExchange,cardNo:" + cardNo);
 | 
				
			||||||
 | 
					            mEMVOptV2.importDataExchangeStatus(0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onTermRiskManagement() throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onTermRiskManagement");
 | 
				
			||||||
 | 
					            mEMVOptV2.importTermRiskManagementStatus(0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onPreFirstGenAC() throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onPreFirstGenAC");
 | 
				
			||||||
 | 
					            mEMVOptV2.importPreFirstGenACStatus(0);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onDataStorageProc(String[] containerID, String[] containerContent) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "onDataStorageProc");
 | 
				
			||||||
 | 
					            String[] tags = new String[0];
 | 
				
			||||||
 | 
					            String[] values = new String[0];
 | 
				
			||||||
 | 
					            mEMVOptV2.importDataStorage(tags, values);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -0,0 +1,121 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.transaction.managers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.view.View;
 | 
				
			||||||
 | 
					import android.view.animation.Animation;
 | 
				
			||||||
 | 
					import android.view.animation.AnimationUtils;
 | 
				
			||||||
 | 
					import android.widget.FrameLayout;
 | 
				
			||||||
 | 
					import android.widget.ImageView;
 | 
				
			||||||
 | 
					import android.widget.TextView;
 | 
				
			||||||
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.R;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * ModalManager - Handles modal UI operations
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class ModalManager {
 | 
				
			||||||
 | 
					    private static final String TAG = "ModalManager";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private FrameLayout modalOverlay;
 | 
				
			||||||
 | 
					    private TextView modalText;
 | 
				
			||||||
 | 
					    private ImageView modalIcon;
 | 
				
			||||||
 | 
					    private Animation fadeIn;
 | 
				
			||||||
 | 
					    private Animation fadeOut;
 | 
				
			||||||
 | 
					    private boolean isModalShowing = false;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public ModalManager(FrameLayout modalOverlay, TextView modalText, ImageView modalIcon) {
 | 
				
			||||||
 | 
					        this.modalOverlay = modalOverlay;
 | 
				
			||||||
 | 
					        this.modalText = modalText;
 | 
				
			||||||
 | 
					        this.modalIcon = modalIcon;
 | 
				
			||||||
 | 
					        initAnimations();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void initAnimations() {
 | 
				
			||||||
 | 
					        fadeIn = AnimationUtils.loadAnimation(modalOverlay.getContext(), android.R.anim.fade_in);
 | 
				
			||||||
 | 
					        fadeOut = AnimationUtils.loadAnimation(modalOverlay.getContext(), android.R.anim.fade_out);
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        fadeIn.setDuration(300);
 | 
				
			||||||
 | 
					        fadeOut.setDuration(300);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void showScanCardModal() {
 | 
				
			||||||
 | 
					        if (isModalShowing) return;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        modalOverlay.post(() -> {
 | 
				
			||||||
 | 
					            modalText.setText("Silakan Tempelkan / Gesekkan / Masukkan Kartu ke Perangkat");
 | 
				
			||||||
 | 
					            modalIcon.setImageResource(R.drawable.ic_card_insert);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            modalOverlay.setVisibility(View.VISIBLE);
 | 
				
			||||||
 | 
					            modalOverlay.startAnimation(fadeIn);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            isModalShowing = true;
 | 
				
			||||||
 | 
					            Log.d(TAG, "Modal scan card shown");
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void showProcessingModal(String message) {
 | 
				
			||||||
 | 
					        if (!isModalShowing) {
 | 
				
			||||||
 | 
					            modalOverlay.post(() -> {
 | 
				
			||||||
 | 
					                modalText.setText(message);
 | 
				
			||||||
 | 
					                modalIcon.setImageResource(R.drawable.ic_card_insert);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                modalOverlay.setVisibility(View.VISIBLE);
 | 
				
			||||||
 | 
					                modalOverlay.startAnimation(fadeIn);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                isModalShowing = true;
 | 
				
			||||||
 | 
					                Log.d(TAG, "Modal processing shown: " + message);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // Just update text if modal already showing
 | 
				
			||||||
 | 
					            modalOverlay.post(() -> {
 | 
				
			||||||
 | 
					                modalText.setText(message);
 | 
				
			||||||
 | 
					                Log.d(TAG, "Modal text updated: " + message);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void hideModal() {
 | 
				
			||||||
 | 
					        if (!isModalShowing) return;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        modalOverlay.post(() -> {
 | 
				
			||||||
 | 
					            fadeOut.setAnimationListener(new Animation.AnimationListener() {
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void onAnimationStart(Animation animation) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void onAnimationEnd(Animation animation) {
 | 
				
			||||||
 | 
					                    modalOverlay.setVisibility(View.GONE);
 | 
				
			||||||
 | 
					                    isModalShowing = false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                @Override
 | 
				
			||||||
 | 
					                public void onAnimationRepeat(Animation animation) {}
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            modalOverlay.startAnimation(fadeOut);
 | 
				
			||||||
 | 
					            Log.d(TAG, "Modal hidden");
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public boolean isShowing() {
 | 
				
			||||||
 | 
					        return isModalShowing;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void updateText(String text) {
 | 
				
			||||||
 | 
					        if (isModalShowing) {
 | 
				
			||||||
 | 
					            modalOverlay.post(() -> {
 | 
				
			||||||
 | 
					                modalText.setText(text);
 | 
				
			||||||
 | 
					                Log.d(TAG, "Modal text updated: " + text);
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void updateIcon(int iconResource) {
 | 
				
			||||||
 | 
					        if (isModalShowing) {
 | 
				
			||||||
 | 
					            modalOverlay.post(() -> {
 | 
				
			||||||
 | 
					                modalIcon.setImageResource(iconResource);
 | 
				
			||||||
 | 
					                Log.d(TAG, "Modal icon updated");
 | 
				
			||||||
 | 
					            });
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,142 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.transaction.managers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.os.RemoteException;
 | 
				
			||||||
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.MyApplication;
 | 
				
			||||||
 | 
					import com.example.bdkipoc.utils.ByteUtil;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.AidlErrorCodeV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.bean.PinPadConfigV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadListenerV2;
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.pinpad.PinPadOptV2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * PinPadManager - Handles PIN pad operations
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class PinPadManager {
 | 
				
			||||||
 | 
					    private static final String TAG = "PinPadManager";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private PinPadOptV2 mPinPadOptV2;
 | 
				
			||||||
 | 
					    private PinPadManagerCallback callback;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public interface PinPadManagerCallback {
 | 
				
			||||||
 | 
					        void onPinInputLength(int length);
 | 
				
			||||||
 | 
					        void onPinInputConfirmed(byte[] pinBlock);
 | 
				
			||||||
 | 
					        void onPinInputCancelled();
 | 
				
			||||||
 | 
					        void onPinInputError(int code, String message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public PinPadManager(PinPadManagerCallback callback) {
 | 
				
			||||||
 | 
					        this.callback = callback;
 | 
				
			||||||
 | 
					        initPinPadComponents();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private void initPinPadComponents() {
 | 
				
			||||||
 | 
					        if (MyApplication.app != null) {
 | 
				
			||||||
 | 
					            mPinPadOptV2 = MyApplication.app.pinPadOptV2;
 | 
				
			||||||
 | 
					            Log.d(TAG, "PIN Pad components initialized");
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            Log.e(TAG, "MyApplication.app is null");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void initPinPad(String cardNo, int pinType) {
 | 
				
			||||||
 | 
					        Log.d(TAG, "========== PIN PAD INITIALIZATION ==========");
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (mPinPadOptV2 == null) {
 | 
				
			||||||
 | 
					                throw new IllegalStateException("PIN Pad service not available");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (cardNo == null || cardNo.length() < 13) {
 | 
				
			||||||
 | 
					                throw new IllegalArgumentException("Invalid card number for PIN");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            PinPadConfigV2 pinPadConfig = new PinPadConfigV2();
 | 
				
			||||||
 | 
					            pinPadConfig.setPinPadType(0);
 | 
				
			||||||
 | 
					            pinPadConfig.setPinType(pinType);
 | 
				
			||||||
 | 
					            pinPadConfig.setOrderNumKey(true);  // Set to true for normal order, false for random
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            String panForPin = cardNo.substring(cardNo.length() - 13, cardNo.length() - 1);
 | 
				
			||||||
 | 
					            byte[] panBytes = panForPin.getBytes("US-ASCII");
 | 
				
			||||||
 | 
					            pinPadConfig.setPan(panBytes);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            pinPadConfig.setTimeout(60 * 1000);
 | 
				
			||||||
 | 
					            pinPadConfig.setPinKeyIndex(12);
 | 
				
			||||||
 | 
					            pinPadConfig.setMaxInput(12);
 | 
				
			||||||
 | 
					            pinPadConfig.setMinInput(0);
 | 
				
			||||||
 | 
					            pinPadConfig.setKeySystem(0);
 | 
				
			||||||
 | 
					            pinPadConfig.setAlgorithmType(0);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            Log.d(TAG, "Initializing PIN pad with config");
 | 
				
			||||||
 | 
					            mPinPadOptV2.initPinPad(pinPadConfig, mPinPadListener);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "PIN pad initialization failed: " + e.getMessage());
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onPinInputError(-1, "PIN Error: " + e.getMessage());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public void cancelPinInput() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            if (mPinPadOptV2 != null) {
 | 
				
			||||||
 | 
					                // Cancel PIN input if needed
 | 
				
			||||||
 | 
					                Log.d(TAG, "PIN input cancelled");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.e(TAG, "Error cancelling PIN input: " + e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // PIN Pad Listener
 | 
				
			||||||
 | 
					    private final PinPadListenerV2 mPinPadListener = new PinPadListenerV2.Stub() {
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onPinLength(int len) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "PIN input length: " + len);
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onPinInputLength(len);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onConfirm(int i, byte[] pinBlock) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "PIN input confirmed");
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            if (pinBlock != null) {
 | 
				
			||||||
 | 
					                String hexStr = ByteUtil.bytes2HexStr(pinBlock);
 | 
				
			||||||
 | 
					                Log.d(TAG, "PIN block received: " + hexStr);
 | 
				
			||||||
 | 
					                if (callback != null) {
 | 
				
			||||||
 | 
					                    callback.onPinInputConfirmed(pinBlock);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                Log.d(TAG, "PIN bypass confirmed");
 | 
				
			||||||
 | 
					                if (callback != null) {
 | 
				
			||||||
 | 
					                    callback.onPinInputConfirmed(null);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onCancel() throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "PIN input cancelled by user");
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onPinInputCancelled();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onError(int code) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.e(TAG, "PIN pad error: " + code);
 | 
				
			||||||
 | 
					            String msg = AidlErrorCodeV2.valueOf(code).getMsg();
 | 
				
			||||||
 | 
					            if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onPinInputError(code, msg);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        public void onHover(int event, byte[] data) throws RemoteException {
 | 
				
			||||||
 | 
					            Log.d(TAG, "PIN pad hover event: " + event);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,358 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.transaction.managers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Context;
 | 
				
			||||||
 | 
					import android.os.AsyncTask;
 | 
				
			||||||
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import org.json.JSONException;
 | 
				
			||||||
 | 
					import org.json.JSONObject;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.io.BufferedReader;
 | 
				
			||||||
 | 
					import java.io.InputStreamReader;
 | 
				
			||||||
 | 
					import java.io.OutputStream;
 | 
				
			||||||
 | 
					import java.net.HttpURLConnection;
 | 
				
			||||||
 | 
					import java.net.URI;
 | 
				
			||||||
 | 
					import java.net.URL;
 | 
				
			||||||
 | 
					import java.util.UUID;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * PostTransactionBackendManager - Handles backend transaction posting
 | 
				
			||||||
 | 
					 * 
 | 
				
			||||||
 | 
					 * This manager handles the communication with the backend service for transaction posting
 | 
				
			||||||
 | 
					 * and provides the transaction_uuid needed for Midtrans integration.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					public class PostTransactionBackendManager {
 | 
				
			||||||
 | 
					    private static final String TAG = "PostTransactionBackend";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Backend Configuration
 | 
				
			||||||
 | 
					    private static final String BACKEND_BASE_URL = "https://be-edc.msvc.app";
 | 
				
			||||||
 | 
					    private static final String TRANSACTIONS_ENDPOINT = BACKEND_BASE_URL + "/transactions";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // Default values
 | 
				
			||||||
 | 
					    private static final String DEFAULT_DEVICE_CODE = "PB4K252T00021";
 | 
				
			||||||
 | 
					    private static final int DEFAULT_DEVICE_ID = 1;
 | 
				
			||||||
 | 
					    private static final String DEFAULT_CASHFLOW = "MONEY_IN";
 | 
				
			||||||
 | 
					    private static final String DEFAULT_CHANNEL_CATEGORY = "RETAIL_OUTLET";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    // ✅ NEW: Static merchant data
 | 
				
			||||||
 | 
					    private static final String DEFAULT_MERCHANT_NAME = "BUDIAJAIB123";
 | 
				
			||||||
 | 
					    private static final String DEFAULT_MID = "542531513";
 | 
				
			||||||
 | 
					    private static final String DEFAULT_TID = "535151521";
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    private Context context;
 | 
				
			||||||
 | 
					    private PostTransactionCallback callback;
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public interface PostTransactionCallback {
 | 
				
			||||||
 | 
					        void onPostTransactionSuccess(JSONObject response, String transactionUuid);
 | 
				
			||||||
 | 
					        void onPostTransactionError(String errorMessage);
 | 
				
			||||||
 | 
					        void onPostTransactionProgress(String message);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    public PostTransactionBackendManager(Context context, PostTransactionCallback callback) {
 | 
				
			||||||
 | 
					        this.context = context;
 | 
				
			||||||
 | 
					        this.callback = callback;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Post transaction to backend service
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void postTransaction(String paymentType, String referenceId, long amount, String status) {
 | 
				
			||||||
 | 
					        String channelCode = mapPaymentTypeToChannelCode(paymentType);
 | 
				
			||||||
 | 
					        String transactionUuid = generateUUID();
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        Log.d(TAG, "=== POSTING TRANSACTION TO BACKEND ===");
 | 
				
			||||||
 | 
					        Log.d(TAG, "Payment Type: " + paymentType);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Channel Code: " + channelCode);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Reference ID: " + referenceId);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Amount: " + amount);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Status: " + status);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Transaction UUID: " + transactionUuid);
 | 
				
			||||||
 | 
					        Log.d(TAG, "=====================================");
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        if (callback != null) {
 | 
				
			||||||
 | 
					            callback.onPostTransactionProgress("Posting transaction to backend...");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        new PostTransactionTask(paymentType, channelCode, referenceId, amount, status, transactionUuid).execute();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Post transaction with INIT status (for pre-authorization)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void postInitTransaction(String paymentType, String referenceId, long amount) {
 | 
				
			||||||
 | 
					        postTransaction(paymentType, referenceId, amount, "INIT");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Post transaction with SUCCESS status (for completed transactions)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void postSuccessTransaction(String paymentType, String referenceId, long amount) {
 | 
				
			||||||
 | 
					        postTransaction(paymentType, referenceId, amount, "SUCCESS");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Update existing transaction status
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void updateTransactionStatus(String transactionUuid, String newStatus) {
 | 
				
			||||||
 | 
					        Log.d(TAG, "Updating transaction " + transactionUuid + " to status: " + newStatus);
 | 
				
			||||||
 | 
					        // TODO: Implement update endpoint if available
 | 
				
			||||||
 | 
					        // For now, we'll create a new transaction with updated status
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Map payment type to channel code for backend
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String mapPaymentTypeToChannelCode(String paymentType) {
 | 
				
			||||||
 | 
					        if (paymentType == null) {
 | 
				
			||||||
 | 
					            return "CREDIT_CARD"; // Default
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        switch (paymentType.toLowerCase()) {
 | 
				
			||||||
 | 
					            case "credit_card":
 | 
				
			||||||
 | 
					                return "CREDIT_CARD";
 | 
				
			||||||
 | 
					            case "debit_card":
 | 
				
			||||||
 | 
					                return "DEBIT_CARD";
 | 
				
			||||||
 | 
					            case "e_money":
 | 
				
			||||||
 | 
					                return "E_MONEY";
 | 
				
			||||||
 | 
					            case "qris":
 | 
				
			||||||
 | 
					                return "QRIS";
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                Log.w(TAG, "Unknown payment type: " + paymentType + ", using CREDIT_CARD");
 | 
				
			||||||
 | 
					                return "CREDIT_CARD";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Generate UUID v4 for transaction
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String generateUUID() {
 | 
				
			||||||
 | 
					        return UUID.randomUUID().toString();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Get device serial number (Sunmi device code)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private String getDeviceCode() {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            // Try to get actual device serial number
 | 
				
			||||||
 | 
					            // For Sunmi devices, this might be available through system properties
 | 
				
			||||||
 | 
					            String serialNumber = android.os.Build.SERIAL;
 | 
				
			||||||
 | 
					            if (serialNumber != null && !serialNumber.equals("unknown") && !serialNumber.equals(android.os.Build.UNKNOWN)) {
 | 
				
			||||||
 | 
					                return serialNumber;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        } catch (Exception e) {
 | 
				
			||||||
 | 
					            Log.w(TAG, "Could not get device serial number: " + e.getMessage());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        // Fallback to default device code
 | 
				
			||||||
 | 
					        return DEFAULT_DEVICE_CODE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * AsyncTask for posting transaction to backend
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private class PostTransactionTask extends AsyncTask<Void, Void, Boolean> {
 | 
				
			||||||
 | 
					        private String paymentType;
 | 
				
			||||||
 | 
					        private String channelCode;
 | 
				
			||||||
 | 
					        private String referenceId;
 | 
				
			||||||
 | 
					        private long amount;
 | 
				
			||||||
 | 
					        private String status;
 | 
				
			||||||
 | 
					        private String transactionUuid;
 | 
				
			||||||
 | 
					        private String errorMessage;
 | 
				
			||||||
 | 
					        private JSONObject responseData;
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        public PostTransactionTask(String paymentType, String channelCode, String referenceId, 
 | 
				
			||||||
 | 
					                                 long amount, String status, String transactionUuid) {
 | 
				
			||||||
 | 
					            this.paymentType = paymentType;
 | 
				
			||||||
 | 
					            this.channelCode = channelCode;
 | 
				
			||||||
 | 
					            this.referenceId = referenceId;
 | 
				
			||||||
 | 
					            this.amount = amount;
 | 
				
			||||||
 | 
					            this.status = status;
 | 
				
			||||||
 | 
					            this.transactionUuid = transactionUuid;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        protected Boolean doInBackground(Void... voids) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                // Build transaction payload
 | 
				
			||||||
 | 
					                JSONObject payload = buildTransactionPayload();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                Log.d(TAG, "Backend payload: " + payload.toString());
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Make HTTP request
 | 
				
			||||||
 | 
					                return makeBackendRequest(payload);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "Backend transaction exception: " + e.getMessage(), e);
 | 
				
			||||||
 | 
					                errorMessage = "Backend transaction error: " + e.getMessage();
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        @Override
 | 
				
			||||||
 | 
					        protected void onPostExecute(Boolean success) {
 | 
				
			||||||
 | 
					            if (success && responseData != null && callback != null) {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    // Extract transaction_uuid from response
 | 
				
			||||||
 | 
					                    JSONObject data = responseData.optJSONObject("data");
 | 
				
			||||||
 | 
					                    String returnedUuid = null;
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if (data != null) {
 | 
				
			||||||
 | 
					                        returnedUuid = data.optString("transaction_uuid", transactionUuid);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    Log.d(TAG, "✅ Backend transaction successful!");
 | 
				
			||||||
 | 
					                    Log.d(TAG, "Original UUID: " + transactionUuid);
 | 
				
			||||||
 | 
					                    Log.d(TAG, "Returned UUID: " + returnedUuid);
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    callback.onPostTransactionSuccess(responseData, returnedUuid != null ? returnedUuid : transactionUuid);
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                } catch (Exception e) {
 | 
				
			||||||
 | 
					                    Log.e(TAG, "Error processing backend response: " + e.getMessage());
 | 
				
			||||||
 | 
					                    if (callback != null) {
 | 
				
			||||||
 | 
					                        callback.onPostTransactionError("Error processing backend response: " + e.getMessage());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            } else if (callback != null) {
 | 
				
			||||||
 | 
					                callback.onPostTransactionError(errorMessage != null ? errorMessage : "Unknown backend error");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        private JSONObject buildTransactionPayload() throws JSONException {
 | 
				
			||||||
 | 
					            JSONObject payload = new JSONObject();
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // Required fields
 | 
				
			||||||
 | 
					            payload.put("type", "PAYMENT");
 | 
				
			||||||
 | 
					            payload.put("channel_category", DEFAULT_CHANNEL_CATEGORY);
 | 
				
			||||||
 | 
					            payload.put("channel_code", channelCode);
 | 
				
			||||||
 | 
					            payload.put("reference_id", referenceId);
 | 
				
			||||||
 | 
					            payload.put("amount", amount);
 | 
				
			||||||
 | 
					            payload.put("cashflow", DEFAULT_CASHFLOW);
 | 
				
			||||||
 | 
					            payload.put("status", status);
 | 
				
			||||||
 | 
					            payload.put("device_id", DEFAULT_DEVICE_ID);
 | 
				
			||||||
 | 
					            payload.put("transaction_uuid", transactionUuid);
 | 
				
			||||||
 | 
					            payload.put("transaction_time_seconds", 2.2); // Default value as mentioned
 | 
				
			||||||
 | 
					            payload.put("device_code", getDeviceCode());
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            // ✅ NEW: Static merchant data (no longer null)
 | 
				
			||||||
 | 
					            payload.put("merchant_name", DEFAULT_MERCHANT_NAME);
 | 
				
			||||||
 | 
					            payload.put("mid", DEFAULT_MID);
 | 
				
			||||||
 | 
					            payload.put("tid", DEFAULT_TID);
 | 
				
			||||||
 | 
					            
 | 
				
			||||||
 | 
					            return payload;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        
 | 
				
			||||||
 | 
					        private Boolean makeBackendRequest(JSONObject payload) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                URL url = new URI(TRANSACTIONS_ENDPOINT).toURL();
 | 
				
			||||||
 | 
					                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Set request properties
 | 
				
			||||||
 | 
					                conn.setRequestMethod("POST");
 | 
				
			||||||
 | 
					                conn.setRequestProperty("Accept", "*/*");
 | 
				
			||||||
 | 
					                conn.setRequestProperty("Content-Type", "application/json");
 | 
				
			||||||
 | 
					                conn.setDoOutput(true);
 | 
				
			||||||
 | 
					                conn.setConnectTimeout(30000);
 | 
				
			||||||
 | 
					                conn.setReadTimeout(30000);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Send payload
 | 
				
			||||||
 | 
					                try (OutputStream os = conn.getOutputStream()) {
 | 
				
			||||||
 | 
					                    byte[] input = payload.toString().getBytes("utf-8");
 | 
				
			||||||
 | 
					                    os.write(input, 0, input.length);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Get response
 | 
				
			||||||
 | 
					                int responseCode = conn.getResponseCode();
 | 
				
			||||||
 | 
					                Log.d(TAG, "Backend response code: " + responseCode);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                BufferedReader br;
 | 
				
			||||||
 | 
					                StringBuilder response = new StringBuilder();
 | 
				
			||||||
 | 
					                String responseLine;
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                if (responseCode >= 200 && responseCode < 300) {
 | 
				
			||||||
 | 
					                    br = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"));
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    br = new BufferedReader(new InputStreamReader(conn.getErrorStream(), "utf-8"));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                while ((responseLine = br.readLine()) != null) {
 | 
				
			||||||
 | 
					                    response.append(responseLine.trim());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                String responseString = response.toString();
 | 
				
			||||||
 | 
					                Log.d(TAG, "Backend response: " + responseString);
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					                // Parse response
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    responseData = new JSONObject(responseString);
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    // Check if response indicates success
 | 
				
			||||||
 | 
					                    int status = responseData.optInt("status", 0);
 | 
				
			||||||
 | 
					                    String message = responseData.optString("message", "");
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                    if (status == 200 && "Successfully".equals(message)) {
 | 
				
			||||||
 | 
					                        return true;
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        errorMessage = "Backend error: " + message + " (Status: " + status + ")";
 | 
				
			||||||
 | 
					                        return false;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    
 | 
				
			||||||
 | 
					                } catch (JSONException e) {
 | 
				
			||||||
 | 
					                    Log.e(TAG, "Error parsing backend response: " + e.getMessage());
 | 
				
			||||||
 | 
					                    errorMessage = "Invalid backend response format";
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					            } catch (Exception e) {
 | 
				
			||||||
 | 
					                Log.e(TAG, "Backend request exception: " + e.getMessage(), e);
 | 
				
			||||||
 | 
					                errorMessage = "Network error: " + e.getMessage();
 | 
				
			||||||
 | 
					                return false;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Utility method to generate reference ID
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String generateReferenceId() {
 | 
				
			||||||
 | 
					        return "ref" + System.currentTimeMillis() + (int)(Math.random() * 10000);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Utility method to map card menu ID to payment type
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String mapCardMenuToPaymentType(int cardMenuId) {
 | 
				
			||||||
 | 
					        // Based on MainActivity.java card IDs
 | 
				
			||||||
 | 
					        switch (cardMenuId) {
 | 
				
			||||||
 | 
					            case 2131296346: // R.id.card_kartu_kredit
 | 
				
			||||||
 | 
					                return "credit_card";
 | 
				
			||||||
 | 
					            case 2131296344: // R.id.card_kartu_debit  
 | 
				
			||||||
 | 
					                return "debit_card";
 | 
				
			||||||
 | 
					            case 2131296360: // R.id.card_uang_elektronik
 | 
				
			||||||
 | 
					                return "e_money";
 | 
				
			||||||
 | 
					            case 2131296352: // R.id.card_qris
 | 
				
			||||||
 | 
					                return "qris";
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                Log.w(TAG, "Unknown card menu ID: " + cardMenuId + ", defaulting to credit_card");
 | 
				
			||||||
 | 
					                return "credit_card";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Debug method to log transaction details
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public void debugTransactionData(String paymentType, String referenceId, long amount, String status) {
 | 
				
			||||||
 | 
					        Log.d(TAG, "=== TRANSACTION DEBUG INFO ===");
 | 
				
			||||||
 | 
					        Log.d(TAG, "Payment Type: " + paymentType);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Channel Code: " + mapPaymentTypeToChannelCode(paymentType));
 | 
				
			||||||
 | 
					        Log.d(TAG, "Reference ID: " + referenceId);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Amount: " + amount);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Status: " + status);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Device Code: " + getDeviceCode());
 | 
				
			||||||
 | 
					        Log.d(TAG, "Device ID: " + DEFAULT_DEVICE_ID);
 | 
				
			||||||
 | 
					        Log.d(TAG, "Merchant Name: " + DEFAULT_MERCHANT_NAME);
 | 
				
			||||||
 | 
					        Log.d(TAG, "MID: " + DEFAULT_MID);
 | 
				
			||||||
 | 
					        Log.d(TAG, "TID: " + DEFAULT_TID);
 | 
				
			||||||
 | 
					        Log.d(TAG, "==============================");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										265
									
								
								app/src/main/java/com/example/bdkipoc/utils/ByteUtil.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										265
									
								
								app/src/main/java/com/example/bdkipoc/utils/ByteUtil.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,265 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.text.TextUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.Arrays;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Locale;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class ByteUtil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** 打印内容 */
 | 
				
			||||||
 | 
					    public static String byte2PrintHex(byte[] raw, int offset, int count) {
 | 
				
			||||||
 | 
					        if (raw == null) {
 | 
				
			||||||
 | 
					            return null;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (offset < 0 || offset > raw.length) {
 | 
				
			||||||
 | 
					            offset = 0;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        int end = offset + count;
 | 
				
			||||||
 | 
					        if (end > raw.length) {
 | 
				
			||||||
 | 
					            end = raw.length;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        StringBuilder hex = new StringBuilder();
 | 
				
			||||||
 | 
					        for (int i = offset; i < end; i++) {
 | 
				
			||||||
 | 
					            int v = raw[i] & 0xFF;
 | 
				
			||||||
 | 
					            String hv = Integer.toHexString(v);
 | 
				
			||||||
 | 
					            if (hv.length() < 2) {
 | 
				
			||||||
 | 
					                hex.append(0);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            hex.append(hv);
 | 
				
			||||||
 | 
					            hex.append(" ");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (hex.length() > 0) {
 | 
				
			||||||
 | 
					            hex.deleteCharAt(hex.length() - 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return hex.toString().toUpperCase();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将字节数组转换成16进制字符串
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param bytes 源字节数组
 | 
				
			||||||
 | 
					     * @return 转换后的16进制字符串
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String bytes2HexStr(byte... bytes) {
 | 
				
			||||||
 | 
					        if (bytes == null || bytes.length == 0) {
 | 
				
			||||||
 | 
					            return "";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return bytes2HexStr(bytes, 0, bytes.length);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将字节数组转换成16进制字符串
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param src    源字节数组
 | 
				
			||||||
 | 
					     * @param offset 偏移量
 | 
				
			||||||
 | 
					     * @param len    数据长度
 | 
				
			||||||
 | 
					     * @return 转换后的16进制字符串
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String bytes2HexStr(byte[] src, int offset, int len) {
 | 
				
			||||||
 | 
					        int end = offset + len;
 | 
				
			||||||
 | 
					        if (src == null || src.length == 0 || offset < 0 || len < 0 || end > src.length) {
 | 
				
			||||||
 | 
					            return "";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        byte[] buffer = new byte[len * 2];
 | 
				
			||||||
 | 
					        int h = 0, l = 0;
 | 
				
			||||||
 | 
					        for (int i = offset, j = 0; i < end; i++) {
 | 
				
			||||||
 | 
					            h = src[i] >> 4 & 0x0f;
 | 
				
			||||||
 | 
					            l = src[i] & 0x0f;
 | 
				
			||||||
 | 
					            buffer[j++] = (byte) (h > 9 ? h - 10 + 'A' : h + '0');
 | 
				
			||||||
 | 
					            buffer[j++] = (byte) (l > 9 ? l - 10 + 'A' : l + '0');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return new String(buffer);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static byte[] hexStr2Bytes(String hexStr) {
 | 
				
			||||||
 | 
					        if (TextUtils.isEmpty(hexStr)) {
 | 
				
			||||||
 | 
					            return new byte[0];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        int length = hexStr.length() / 2;
 | 
				
			||||||
 | 
					        char[] chars = hexStr.toCharArray();
 | 
				
			||||||
 | 
					        byte[] b = new byte[length];
 | 
				
			||||||
 | 
					        for (int i = 0; i < length; i++) {
 | 
				
			||||||
 | 
					            b[i] = (byte) (char2Byte(chars[i * 2]) << 4 | char2Byte(chars[i * 2 + 1]));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return b;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static byte hexStr2Byte(String hexStr) {
 | 
				
			||||||
 | 
					        return (byte) Integer.parseInt(hexStr, 16);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static String hexStr2Str(String hexStr) {
 | 
				
			||||||
 | 
					        String vi = "0123456789ABC DEF".trim();
 | 
				
			||||||
 | 
					        char[] array = hexStr.toCharArray();
 | 
				
			||||||
 | 
					        byte[] bytes = new byte[hexStr.length() / 2];
 | 
				
			||||||
 | 
					        int temp;
 | 
				
			||||||
 | 
					        for (int i = 0; i < bytes.length; i++) {
 | 
				
			||||||
 | 
					            char c = array[2 * i];
 | 
				
			||||||
 | 
					            temp = vi.indexOf(c) * 16;
 | 
				
			||||||
 | 
					            c = array[2 * i + 1];
 | 
				
			||||||
 | 
					            temp += vi.indexOf(c);
 | 
				
			||||||
 | 
					            bytes[i] = (byte) (temp & 0xFF);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return new String(bytes);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static String hexStr2AsciiStr(String hexStr) {
 | 
				
			||||||
 | 
					        String vi = "0123456789ABC DEF".trim();
 | 
				
			||||||
 | 
					        hexStr = hexStr.trim().replace(" ", "").toUpperCase(Locale.US);
 | 
				
			||||||
 | 
					        char[] array = hexStr.toCharArray();
 | 
				
			||||||
 | 
					        byte[] bytes = new byte[hexStr.length() / 2];
 | 
				
			||||||
 | 
					        int temp = 0x00;
 | 
				
			||||||
 | 
					        for (int i = 0; i < bytes.length; i++) {
 | 
				
			||||||
 | 
					            char c = array[2 * i];
 | 
				
			||||||
 | 
					            temp = vi.indexOf(c) << 4;
 | 
				
			||||||
 | 
					            c = array[2 * i + 1];
 | 
				
			||||||
 | 
					            temp |= vi.indexOf(c);
 | 
				
			||||||
 | 
					            bytes[i] = (byte) (temp & 0xFF);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return new String(bytes);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将无符号short转换成int,大端模式(高位在前)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static int unsignedShort2IntBE(byte[] src, int offset) {
 | 
				
			||||||
 | 
					        return (src[offset] & 0xff) << 8 | (src[offset + 1] & 0xff);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将无符号short转换成int,小端模式(低位在前)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static int unsignedShort2IntLE(byte[] src, int offset) {
 | 
				
			||||||
 | 
					        return (src[offset] & 0xff) | (src[offset + 1] & 0xff) << 8;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将无符号byte转换成int
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static int unsignedByte2Int(byte[] src, int offset) {
 | 
				
			||||||
 | 
					        return src[offset] & 0xFF;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将字节数组转换成int,大端模式(高位在前)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static int unsignedInt2IntBE(byte[] src, int offset) {
 | 
				
			||||||
 | 
					        int result = 0;
 | 
				
			||||||
 | 
					        for (int i = offset; i < offset + 4; i++) {
 | 
				
			||||||
 | 
					            result |= (src[i] & 0xff) << (offset + 3 - i) * 8;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将字节数组转换成int,小端模式(低位在前)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static int unsignedInt2IntLE(byte[] src, int offset) {
 | 
				
			||||||
 | 
					        int value = 0;
 | 
				
			||||||
 | 
					        for (int i = offset; i < offset + 4; i++) {
 | 
				
			||||||
 | 
					            value |= (src[i] & 0xff) << (i - offset) * 8;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return value;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将int转换成byte数组,大端模式(高位在前)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static byte[] int2BytesBE(int src) {
 | 
				
			||||||
 | 
					        byte[] result = new byte[4];
 | 
				
			||||||
 | 
					        for (int i = 0; i < 4; i++) {
 | 
				
			||||||
 | 
					            result[i] = (byte) (src >> (3 - i) * 8);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将int转换成byte数组,小端模式(低位在前)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static byte[] int2BytesLE(int src) {
 | 
				
			||||||
 | 
					        byte[] result = new byte[4];
 | 
				
			||||||
 | 
					        for (int i = 0; i < 4; i++) {
 | 
				
			||||||
 | 
					            result[i] = (byte) (src >> i * 8);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将short转换成byte数组,大端模式(高位在前)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static byte[] short2BytesBE(short src) {
 | 
				
			||||||
 | 
					        byte[] result = new byte[2];
 | 
				
			||||||
 | 
					        for (int i = 0; i < 2; i++) {
 | 
				
			||||||
 | 
					            result[i] = (byte) (src >> (1 - i) * 8);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将short转换成byte数组,小端模式(低位在前)
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static byte[] short2BytesLE(short src) {
 | 
				
			||||||
 | 
					        byte[] result = new byte[2];
 | 
				
			||||||
 | 
					        for (int i = 0; i < 2; i++) {
 | 
				
			||||||
 | 
					            result[i] = (byte) (src >> i * 8);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将字节数组列表合并成单个字节数组
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static byte[] concatByteArrays(byte[]... list) {
 | 
				
			||||||
 | 
					        if (list == null || list.length == 0) {
 | 
				
			||||||
 | 
					            return new byte[0];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return concatByteArrays(Arrays.asList(list));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 将字节数组列表合并成单个字节数组
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static byte[] concatByteArrays(List<byte[]> list) {
 | 
				
			||||||
 | 
					        if (list == null || list.isEmpty()) {
 | 
				
			||||||
 | 
					            return new byte[0];
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        int totalLen = 0;
 | 
				
			||||||
 | 
					        for (byte[] b : list) {
 | 
				
			||||||
 | 
					            if (b == null || b.length == 0) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            totalLen += b.length;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        byte[] result = new byte[totalLen];
 | 
				
			||||||
 | 
					        int index = 0;
 | 
				
			||||||
 | 
					        for (byte[] b : list) {
 | 
				
			||||||
 | 
					            if (b == null || b.length == 0) {
 | 
				
			||||||
 | 
					                continue;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            System.arraycopy(b, 0, result, index, b.length);
 | 
				
			||||||
 | 
					            index += b.length;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return result;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Convert char to byte
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param c char
 | 
				
			||||||
 | 
					     * @return byte
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    private static int char2Byte(char c) {
 | 
				
			||||||
 | 
					        if (c >= 'a') {
 | 
				
			||||||
 | 
					            return (c - 'a' + 10) & 0x0f;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (c >= 'A') {
 | 
				
			||||||
 | 
					            return (c - 'A' + 10) & 0x0f;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return (c - '0') & 0x0f;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										89
									
								
								app/src/main/java/com/example/bdkipoc/utils/LogUtil.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								app/src/main/java/com/example/bdkipoc/utils/LogUtil.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.text.TextUtils;
 | 
				
			||||||
 | 
					import android.util.Log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class LogUtil {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static final int VERBOSE = 1;
 | 
				
			||||||
 | 
					    public static final int DEBUG = 2;
 | 
				
			||||||
 | 
					    public static final int INFO = 3;
 | 
				
			||||||
 | 
					    public static final int WARN = 4;
 | 
				
			||||||
 | 
					    public static final int ERROR = 5;
 | 
				
			||||||
 | 
					    public static final int NOTHING = 6;
 | 
				
			||||||
 | 
					    public static int LEVEL = VERBOSE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void setLevel(int Level) {
 | 
				
			||||||
 | 
					        LEVEL = Level;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void v(String TAG, String msg) {
 | 
				
			||||||
 | 
					        if (LEVEL <= VERBOSE && !TextUtils.isEmpty(msg)) {
 | 
				
			||||||
 | 
					            MyLog(VERBOSE, TAG, msg);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void d(String TAG, String msg) {
 | 
				
			||||||
 | 
					        if (LEVEL <= DEBUG && !TextUtils.isEmpty(msg)) {
 | 
				
			||||||
 | 
					            MyLog(DEBUG, TAG, msg);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void i(String TAG, String msg) {
 | 
				
			||||||
 | 
					        if (LEVEL <= INFO && !TextUtils.isEmpty(msg)) {
 | 
				
			||||||
 | 
					            MyLog(INFO, TAG, msg);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void w(String TAG, String msg) {
 | 
				
			||||||
 | 
					        if (LEVEL <= WARN && !TextUtils.isEmpty(msg)) {
 | 
				
			||||||
 | 
					            MyLog(WARN, TAG, msg);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static void e(String TAG, String msg) {
 | 
				
			||||||
 | 
					        if (LEVEL <= ERROR && !TextUtils.isEmpty(msg)) {
 | 
				
			||||||
 | 
					            MyLog(ERROR, TAG, msg);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private static void MyLog(int type, String TAG, String msg) {
 | 
				
			||||||
 | 
					        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
 | 
				
			||||||
 | 
					        int index = 4;
 | 
				
			||||||
 | 
					        String className = stackTrace[index].getFileName();
 | 
				
			||||||
 | 
					        String methodName = stackTrace[index].getMethodName();
 | 
				
			||||||
 | 
					        int lineNumber = stackTrace[index].getLineNumber();
 | 
				
			||||||
 | 
					        methodName = methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
 | 
				
			||||||
 | 
					        StringBuilder stringBuilder = new StringBuilder();
 | 
				
			||||||
 | 
					        stringBuilder.append("[ (")
 | 
				
			||||||
 | 
					                .append(className)
 | 
				
			||||||
 | 
					                .append(":")
 | 
				
			||||||
 | 
					                .append(lineNumber)
 | 
				
			||||||
 | 
					                .append(")#")
 | 
				
			||||||
 | 
					                .append(methodName)
 | 
				
			||||||
 | 
					                .append(" ] ");
 | 
				
			||||||
 | 
					        stringBuilder.append(msg);
 | 
				
			||||||
 | 
					        String logStr = stringBuilder.toString();
 | 
				
			||||||
 | 
					        switch (type) {
 | 
				
			||||||
 | 
					            case VERBOSE:
 | 
				
			||||||
 | 
					                Log.v(TAG, logStr);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case DEBUG:
 | 
				
			||||||
 | 
					                Log.d(TAG, logStr);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case INFO:
 | 
				
			||||||
 | 
					                Log.i(TAG, logStr);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case WARN:
 | 
				
			||||||
 | 
					                Log.w(TAG, logStr);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            case ERROR:
 | 
				
			||||||
 | 
					                Log.e(TAG, logStr);
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            default:
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										107
									
								
								app/src/main/java/com/example/bdkipoc/utils/Utility.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								app/src/main/java/com/example/bdkipoc/utils/Utility.java
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,107 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.utils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.os.Handler;
 | 
				
			||||||
 | 
					import android.os.Looper;
 | 
				
			||||||
 | 
					import android.widget.Toast;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.example.bdkipoc.MyApplication;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import java.util.ArrayList;
 | 
				
			||||||
 | 
					import java.util.Collections;
 | 
				
			||||||
 | 
					import java.util.List;
 | 
				
			||||||
 | 
					import java.util.Locale;
 | 
				
			||||||
 | 
					import java.util.regex.Pattern;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public final class Utility {
 | 
				
			||||||
 | 
					    private Utility() {
 | 
				
			||||||
 | 
					        throw new AssertionError("Create instance of Utility is forbidden.");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Bundle对象转换成字符串 */
 | 
				
			||||||
 | 
					    public static String bundle2String(Bundle bundle) {
 | 
				
			||||||
 | 
					        return bundle2String(bundle, 1);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * 根据key排序后将Bundle内容拼接成字符串
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param bundle 要处理的bundle
 | 
				
			||||||
 | 
					     * @param order  排序规则,0-不排序,1-升序,2-降序
 | 
				
			||||||
 | 
					     * @return 拼接后的字符串
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public static String bundle2String(Bundle bundle, int order) {
 | 
				
			||||||
 | 
					        if (bundle == null || bundle.keySet().isEmpty()) {
 | 
				
			||||||
 | 
					            return "";
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        StringBuilder sb = new StringBuilder();
 | 
				
			||||||
 | 
					        List<String> list = new ArrayList<>(bundle.keySet());
 | 
				
			||||||
 | 
					        if (order == 1) { //升序
 | 
				
			||||||
 | 
					            Collections.sort(list, String::compareTo);
 | 
				
			||||||
 | 
					        } else if (order == 2) {//降序
 | 
				
			||||||
 | 
					            Collections.sort(list, Collections.reverseOrder());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        for (String key : list) {
 | 
				
			||||||
 | 
					            sb.append(key);
 | 
				
			||||||
 | 
					            sb.append(":");
 | 
				
			||||||
 | 
					            Object value = bundle.get(key);
 | 
				
			||||||
 | 
					            if (value instanceof byte[]) {
 | 
				
			||||||
 | 
					                sb.append(ByteUtil.bytes2HexStr((byte[]) value));
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                sb.append(value);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            sb.append("\n");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (sb.length() > 0) {
 | 
				
			||||||
 | 
					            sb.deleteCharAt(sb.length() - 1);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return sb.toString();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** 将null转换成空串 */
 | 
				
			||||||
 | 
					    public static String null2String(String str) {
 | 
				
			||||||
 | 
					        return str == null ? "" : str;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static String formatStr(String format, Object... params) {
 | 
				
			||||||
 | 
					        return String.format(Locale.ENGLISH, format, params);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** check whether src is hex format */
 | 
				
			||||||
 | 
					    public static boolean checkHexValue(String src) {
 | 
				
			||||||
 | 
					        return Pattern.matches("[0-9a-fA-F]+", src);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** 显示Toast */
 | 
				
			||||||
 | 
					    public static void showToast(final String msg) {
 | 
				
			||||||
 | 
					        Handler handler = new Handler(Looper.getMainLooper());
 | 
				
			||||||
 | 
					        handler.post(() -> Toast.makeText(MyApplication.app, msg, Toast.LENGTH_SHORT).show());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** 显示Toast */
 | 
				
			||||||
 | 
					    public static void showToast(int resId) {
 | 
				
			||||||
 | 
					        showToast(MyApplication.app.getString(resId));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** 根据结果码获取成功失败信息 */
 | 
				
			||||||
 | 
					    public static String getStateString(int code) {
 | 
				
			||||||
 | 
					        return code == 0 ? "success" : "failed, code:" + code;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** 根据结果状态获取成功失败信息 */
 | 
				
			||||||
 | 
					    public static String getStateString(boolean state) {
 | 
				
			||||||
 | 
					        return state ? "success" : "failed";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** 将dp转成px */
 | 
				
			||||||
 | 
					    public static int dp2px(int dp) {
 | 
				
			||||||
 | 
					        float density = MyApplication.app.getResources().getDisplayMetrics().density;
 | 
				
			||||||
 | 
					        return Math.round(dp * density);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** 将px转成dp */
 | 
				
			||||||
 | 
					    public static int px2dp(int px) {
 | 
				
			||||||
 | 
					        float density = MyApplication.app.getResources().getDisplayMetrics().density;
 | 
				
			||||||
 | 
					        return Math.round(px / density);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					package com.example.bdkipoc.wrapper;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.os.Bundle;
 | 
				
			||||||
 | 
					import android.os.RemoteException;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import com.sunmi.pay.hardware.aidlv2.readcard.CheckCardCallbackV2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public class CheckCardCallbackV2Wrapper extends CheckCardCallbackV2.Stub {
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void findMagCard(Bundle info) throws RemoteException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void findICCard(String atr) throws RemoteException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void findRFCard(String uuid) throws RemoteException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onError(int code, String message) throws RemoteException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void findICCardEx(Bundle info) throws RemoteException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void findRFCardEx(Bundle info) throws RemoteException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void onErrorEx(Bundle info) throws RemoteException {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										10
									
								
								app/src/main/res/anim/slide_down.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/anim/slide_down.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					<set xmlns:android="http://schemas.android.com/apk/res/android">
 | 
				
			||||||
 | 
					    <translate
 | 
				
			||||||
 | 
					        android:duration="300"
 | 
				
			||||||
 | 
					        android:fromYDelta="0%p"
 | 
				
			||||||
 | 
					        android:toYDelta="50%p" />
 | 
				
			||||||
 | 
					    <alpha
 | 
				
			||||||
 | 
					        android:duration="300"
 | 
				
			||||||
 | 
					        android:fromAlpha="1.0"
 | 
				
			||||||
 | 
					        android:toAlpha="0.0" />
 | 
				
			||||||
 | 
					</set>
 | 
				
			||||||
							
								
								
									
										10
									
								
								app/src/main/res/anim/slide_up.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								app/src/main/res/anim/slide_up.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					<set xmlns:android="http://schemas.android.com/apk/res/android">
 | 
				
			||||||
 | 
					    <translate
 | 
				
			||||||
 | 
					        android:duration="300"
 | 
				
			||||||
 | 
					        android:fromYDelta="50%p"
 | 
				
			||||||
 | 
					        android:toYDelta="0%p" />
 | 
				
			||||||
 | 
					    <alpha
 | 
				
			||||||
 | 
					        android:duration="300"
 | 
				
			||||||
 | 
					        android:fromAlpha="0.0"
 | 
				
			||||||
 | 
					        android:toAlpha="1.0" />
 | 
				
			||||||
 | 
					</set>
 | 
				
			||||||
							
								
								
									
										
											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  | 
							
								
								
									
										6
									
								
								app/src/main/res/drawable/bg_status.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								app/src/main/res/drawable/bg_status.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<shape xmlns:android="http://schemas.android.com/apk/res/android">
 | 
				
			||||||
 | 
					    <solid android:color="#F0F0F0"/>
 | 
				
			||||||
 | 
					    <corners android:radius="8dp"/>
 | 
				
			||||||
 | 
					    <stroke android:width="1dp" android:color="#E0E0E0"/>
 | 
				
			||||||
 | 
					</shape>
 | 
				
			||||||
							
								
								
									
										5
									
								
								app/src/main/res/drawable/button_cancel_background.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/res/drawable/button_cancel_background.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					<shape xmlns:android="http://schemas.android.com/apk/res/android">
 | 
				
			||||||
 | 
					    <stroke android:width="2dp" android:color="#E31937"/>
 | 
				
			||||||
 | 
					    <corners android:radius="8dp"/>
 | 
				
			||||||
 | 
					    <solid android:color="@android:color/transparent"/>
 | 
				
			||||||
 | 
					</shape>
 | 
				
			||||||
							
								
								
									
										
											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>
 | 
					 | 
				
			||||||
							
								
								
									
										5
									
								
								app/src/main/res/drawable/timer_circle_background.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								app/src/main/res/drawable/timer_circle_background.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					<shape xmlns:android="http://schemas.android.com/apk/res/android">
 | 
				
			||||||
 | 
					    <solid android:color="#F0F0F0"/>
 | 
				
			||||||
 | 
					    <corners android:radius="24dp"/>
 | 
				
			||||||
 | 
					    <stroke android:width="1dp" android:color="#E0E0E0"/>
 | 
				
			||||||
 | 
					</shape>
 | 
				
			||||||
@ -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.
										
									
								
							
							
								
								
									
										328
									
								
								app/src/main/res/layout/activity_create_transaction.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										328
									
								
								app/src/main/res/layout/activity_create_transaction.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,328 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
					    xmlns:tools="http://schemas.android.com/tools"
 | 
				
			||||||
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					    android:layout_height="match_parent"
 | 
				
			||||||
 | 
					    android:background="#FFFFFF"
 | 
				
			||||||
 | 
					    tools:context=".transaction.CreateTransactionActivity">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Main Content -->
 | 
				
			||||||
 | 
					    <ScrollView
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="match_parent"
 | 
				
			||||||
 | 
					        android:fillViewport="true"
 | 
				
			||||||
 | 
					        android:overScrollMode="never"
 | 
				
			||||||
 | 
					        android:scrollbars="none"
 | 
				
			||||||
 | 
					        android:background="#FFFFFF">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <LinearLayout
 | 
				
			||||||
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:orientation="vertical">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <include layout="@layout/component_appbar" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Payment Card - Positioned to overlap with red header -->
 | 
				
			||||||
 | 
					            <androidx.cardview.widget.CardView
 | 
				
			||||||
 | 
					                android:id="@+id/paymentCard"
 | 
				
			||||||
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                android:layout_height="191dp"
 | 
				
			||||||
 | 
					                android:layout_marginStart="16dp"
 | 
				
			||||||
 | 
					                android:layout_marginEnd="16dp"
 | 
				
			||||||
 | 
					                android:layout_marginTop="-85dp"
 | 
				
			||||||
 | 
					                app:cardBackgroundColor="#3498DB"
 | 
				
			||||||
 | 
					                app:cardCornerRadius="16dp"
 | 
				
			||||||
 | 
					                app:cardElevation="8dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <LinearLayout
 | 
				
			||||||
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                    android:layout_height="match_parent"
 | 
				
			||||||
 | 
					                    android:orientation="vertical"
 | 
				
			||||||
 | 
					                    android:padding="24dp"
 | 
				
			||||||
 | 
					                    android:paddingTop="32dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <!-- Title -->
 | 
				
			||||||
 | 
					                    <TextView
 | 
				
			||||||
 | 
					                        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                        android:text="TOTAL PEMBAYARAN"
 | 
				
			||||||
 | 
					                        android:textColor="@android:color/white"
 | 
				
			||||||
 | 
					                        android:textSize="20sp"
 | 
				
			||||||
 | 
					                        android:textStyle="bold"
 | 
				
			||||||
 | 
					                        android:fontFamily="@font/inter"
 | 
				
			||||||
 | 
					                        android:layout_marginBottom="20dp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <!-- 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" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        <TextView
 | 
				
			||||||
 | 
					                            android:id="@+id/tv_amount_display"
 | 
				
			||||||
 | 
					                            android:layout_width="0dp"
 | 
				
			||||||
 | 
					                            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                            android:layout_weight="1"
 | 
				
			||||||
 | 
					                            android:background="@android:color/transparent"
 | 
				
			||||||
 | 
					                            android:textColor="@android:color/white"
 | 
				
			||||||
 | 
					                            android:textSize="32sp"
 | 
				
			||||||
 | 
					                            android:textStyle="bold"
 | 
				
			||||||
 | 
					                            android:fontFamily="@font/inter"
 | 
				
			||||||
 | 
					                            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="12dp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <!-- 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="10sp"
 | 
				
			||||||
 | 
					                        android:fontFamily="@font/inter"
 | 
				
			||||||
 | 
					                        android:textStyle="normal"
 | 
				
			||||||
 | 
					                        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="32dp"
 | 
				
			||||||
 | 
					                android:layout_marginStart="16dp"
 | 
				
			||||||
 | 
					                android:layout_marginEnd="16dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- Row 1: 1, 2, 3 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/btn_1"
 | 
				
			||||||
 | 
					                    style="@style/NumpadButton"
 | 
				
			||||||
 | 
					                    android:text="1" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/btn_2"
 | 
				
			||||||
 | 
					                    style="@style/NumpadButton"
 | 
				
			||||||
 | 
					                    android:text="2" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/btn_3"
 | 
				
			||||||
 | 
					                    style="@style/NumpadButton"
 | 
				
			||||||
 | 
					                    android:text="3" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- Row 2: 4, 5, 6 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/btn_4"
 | 
				
			||||||
 | 
					                    style="@style/NumpadButton"
 | 
				
			||||||
 | 
					                    android:text="4" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/btn_5"
 | 
				
			||||||
 | 
					                    style="@style/NumpadButton"
 | 
				
			||||||
 | 
					                    android:text="5" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/btn_6"
 | 
				
			||||||
 | 
					                    style="@style/NumpadButton"
 | 
				
			||||||
 | 
					                    android:text="6" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- Row 3: 7, 8, 9 -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/btn_7"
 | 
				
			||||||
 | 
					                    style="@style/NumpadButton"
 | 
				
			||||||
 | 
					                    android:text="7" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/btn_8"
 | 
				
			||||||
 | 
					                    style="@style/NumpadButton"
 | 
				
			||||||
 | 
					                    android:text="8" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/btn_9"
 | 
				
			||||||
 | 
					                    style="@style/NumpadButton"
 | 
				
			||||||
 | 
					                    android:text="9" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <!-- Row 4: 000, 0, Delete -->
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/btn_00"
 | 
				
			||||||
 | 
					                    style="@style/NumpadButton"
 | 
				
			||||||
 | 
					                    android:text="000" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/btn_0"
 | 
				
			||||||
 | 
					                    style="@style/NumpadButton"
 | 
				
			||||||
 | 
					                    android:text="0" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <ImageView
 | 
				
			||||||
 | 
					                    android:id="@+id/btn_clear"
 | 
				
			||||||
 | 
					                    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 -->
 | 
				
			||||||
 | 
					            <com.google.android.material.button.MaterialButton
 | 
				
			||||||
 | 
					                android:id="@+id/btn_confirm"
 | 
				
			||||||
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                android:layout_height="48dp"
 | 
				
			||||||
 | 
					                android:text="Konfirmasi"
 | 
				
			||||||
 | 
					                android:textColor="#FFFFFF"
 | 
				
			||||||
 | 
					                android:textSize="12sp"
 | 
				
			||||||
 | 
					                android:textStyle="bold"
 | 
				
			||||||
 | 
					                android:fontFamily="@font/inter"
 | 
				
			||||||
 | 
					                android:layout_marginStart="16dp"
 | 
				
			||||||
 | 
					                android:layout_marginEnd="16dp"
 | 
				
			||||||
 | 
					                android:layout_marginTop="32dp"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="24dp"
 | 
				
			||||||
 | 
					                app:backgroundTint="@null"
 | 
				
			||||||
 | 
					                android:background="@drawable/button_confirm_background_selector"
 | 
				
			||||||
 | 
					                android:enabled="false"
 | 
				
			||||||
 | 
					                app:cornerRadius="8dp"
 | 
				
			||||||
 | 
					                app:rippleColor="#B3000000" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <!-- Hidden Components -->
 | 
				
			||||||
 | 
					            <TextView
 | 
				
			||||||
 | 
					                android:id="@+id/tv_mode_indicator"
 | 
				
			||||||
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
 | 
					                android:layout_height="0dp"
 | 
				
			||||||
 | 
					                android:visibility="gone" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <Button
 | 
				
			||||||
 | 
					                android:id="@+id/btn_toggle_mode"
 | 
				
			||||||
 | 
					                android:layout_width="0dp"
 | 
				
			||||||
 | 
					                android:layout_height="0dp"
 | 
				
			||||||
 | 
					                android:visibility="gone" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <ProgressBar
 | 
				
			||||||
 | 
					                android:id="@+id/progress_bar"
 | 
				
			||||||
 | 
					                style="?android:attr/progressBarStyle"
 | 
				
			||||||
 | 
					                android:layout_width="48dp"
 | 
				
			||||||
 | 
					                android:layout_height="48dp"
 | 
				
			||||||
 | 
					                android:layout_gravity="center"
 | 
				
			||||||
 | 
					                android:visibility="gone"
 | 
				
			||||||
 | 
					                android:indeterminateTint="#E31937" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </ScrollView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Modal Overlay -->
 | 
				
			||||||
 | 
					    <FrameLayout
 | 
				
			||||||
 | 
					        android:id="@+id/modal_overlay"
 | 
				
			||||||
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
 | 
					        android:layout_height="match_parent"
 | 
				
			||||||
 | 
					        android:background="#80000000"
 | 
				
			||||||
 | 
					        android:visibility="gone"
 | 
				
			||||||
 | 
					        android:clickable="true"
 | 
				
			||||||
 | 
					        android:focusable="true"
 | 
				
			||||||
 | 
					        android:elevation="100dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <androidx.cardview.widget.CardView
 | 
				
			||||||
 | 
					            android:id="@+id/modal_card"
 | 
				
			||||||
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_gravity="center"
 | 
				
			||||||
 | 
					            android:layout_margin="32dp"
 | 
				
			||||||
 | 
					            app:cardCornerRadius="16dp"
 | 
				
			||||||
 | 
					            app:cardElevation="20dp"
 | 
				
			||||||
 | 
					            app:cardBackgroundColor="@android:color/white">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            <LinearLayout
 | 
				
			||||||
 | 
					                android:layout_width="match_parent"
 | 
				
			||||||
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                android:orientation="vertical"
 | 
				
			||||||
 | 
					                android:padding="32dp"
 | 
				
			||||||
 | 
					                android:gravity="center">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <ImageView
 | 
				
			||||||
 | 
					                    android:id="@+id/modal_icon"
 | 
				
			||||||
 | 
					                    android:layout_width="80dp"
 | 
				
			||||||
 | 
					                    android:layout_height="80dp"
 | 
				
			||||||
 | 
					                    android:src="@drawable/ic_card_insert"
 | 
				
			||||||
 | 
					                    android:layout_marginBottom="24dp"
 | 
				
			||||||
 | 
					                    android:scaleType="fitCenter"
 | 
				
			||||||
 | 
					                    android:adjustViewBounds="true"
 | 
				
			||||||
 | 
					                    app:tint="#E31937" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <TextView
 | 
				
			||||||
 | 
					                    android:id="@+id/modal_text"
 | 
				
			||||||
 | 
					                    android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					                    android:text="Silakan Tempelkan / Gesekkan / Masukkan Kartu ke Perangkat"
 | 
				
			||||||
 | 
					                    style="@style/StatusTextStyle"
 | 
				
			||||||
 | 
					                    android:textAlignment="center"
 | 
				
			||||||
 | 
					                    android:gravity="center"
 | 
				
			||||||
 | 
					                    android:lineSpacingExtra="4dp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        </androidx.cardview.widget.CardView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </FrameLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <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"
 | 
				
			||||||
 | 
					        android:elevation="100dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 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>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</FrameLayout>
 | 
				
			||||||
@ -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>
 | 
					 | 
				
			||||||
@ -1,223 +1,241 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
					<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
					    xmlns:app="http://schemas.android.com/apk/res-auto"
 | 
				
			||||||
 | 
					    xmlns:tools="http://schemas.android.com/tools"
 | 
				
			||||||
    android:layout_width="match_parent"
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
    android:layout_height="match_parent"
 | 
					    android:layout_height="match_parent"
 | 
				
			||||||
    android:orientation="vertical"
 | 
					    android:background="#F5F5F5"
 | 
				
			||||||
    android:background="#FFFFFF">
 | 
					    tools:context=".QrisResultActivity">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Red Status Bar -->
 | 
					    <!-- Header Background -->
 | 
				
			||||||
    <View
 | 
					    <View
 | 
				
			||||||
 | 
					        android:id="@+id/header_background"
 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
        android:layout_height="44dp"
 | 
					        android:layout_height="100dp"
 | 
				
			||||||
        android:background="#E31937" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    <!-- Header with Back Navigation -->
 | 
					 | 
				
			||||||
    <LinearLayout
 | 
					 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					 | 
				
			||||||
        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
        android:background="#E31937"
 | 
					        android:background="#E31937"
 | 
				
			||||||
        android:paddingBottom="16dp">
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Back Navigation -->
 | 
				
			||||||
    <LinearLayout
 | 
					    <LinearLayout
 | 
				
			||||||
        android:id="@+id/back_navigation"
 | 
					        android:id="@+id/back_navigation"
 | 
				
			||||||
        android:layout_width="wrap_content"
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:orientation="horizontal"
 | 
					        android:orientation="horizontal"
 | 
				
			||||||
        android:gravity="center_vertical"
 | 
					        android:gravity="center_vertical"
 | 
				
			||||||
            android:layout_marginStart="16dp"
 | 
					        android:padding="16dp"
 | 
				
			||||||
            android:background="?attr/selectableItemBackgroundBorderless"
 | 
					        android:layout_marginTop="24dp"
 | 
				
			||||||
            android:padding="8dp"
 | 
					        android:background="?android:attr/selectableItemBackground"
 | 
				
			||||||
            android:clickable="true"
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
            android:focusable="true">
 | 
					        app:layout_constraintStart_toStartOf="parent">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <TextView
 | 
					        <ImageView
 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					            android:id="@+id/backArrow"
 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					            android:layout_width="24dp"
 | 
				
			||||||
                android:text="‹"
 | 
					            android:layout_height="24dp"
 | 
				
			||||||
                android:textColor="@android:color/white"
 | 
					            android:src="@drawable/ic_arrow_back"
 | 
				
			||||||
                android:textSize="18sp"
 | 
					 | 
				
			||||||
                android:textStyle="bold"
 | 
					 | 
				
			||||||
            android:layout_marginEnd="8dp" />
 | 
					            android:layout_marginEnd="8dp" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <TextView
 | 
					        <TextView
 | 
				
			||||||
 | 
					            android:id="@+id/toolbarTitle"
 | 
				
			||||||
            android:layout_width="wrap_content"
 | 
					            android:layout_width="wrap_content"
 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
                android:text="Kembali"
 | 
					            android:text="Generate QR"
 | 
				
			||||||
            android:textColor="@android:color/white"
 | 
					            android:textColor="@android:color/white"
 | 
				
			||||||
                android:textSize="14sp" />
 | 
					            android:textSize="18sp"
 | 
				
			||||||
        </LinearLayout>
 | 
					            android:textStyle="bold"
 | 
				
			||||||
 | 
					            android:fontFamily="@font/inter" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    </LinearLayout>
 | 
					    </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- White Card Container -->
 | 
					    <!-- Main Content Card -->
 | 
				
			||||||
    <androidx.cardview.widget.CardView
 | 
					    <androidx.cardview.widget.CardView
 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					        android:id="@+id/main_card"
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:layout_margin="16dp"
 | 
					        android:layout_marginStart="16dp"
 | 
				
			||||||
        android:layout_marginTop="0dp"
 | 
					        android:layout_marginEnd="16dp"
 | 
				
			||||||
 | 
					        android:layout_marginTop="16dp"
 | 
				
			||||||
        app:cardCornerRadius="16dp"
 | 
					        app:cardCornerRadius="16dp"
 | 
				
			||||||
        app:cardElevation="8dp"
 | 
					        app:cardElevation="8dp"
 | 
				
			||||||
        app:cardBackgroundColor="@android:color/white">
 | 
					        app:layout_constraintTop_toBottomOf="@id/header_background"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintBottom_toTopOf="@id/cancel_button"
 | 
				
			||||||
 | 
					        app:layout_constraintVertical_bias="0.3">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        <LinearLayout
 | 
					        <LinearLayout
 | 
				
			||||||
            android:layout_width="match_parent"
 | 
					            android:layout_width="match_parent"
 | 
				
			||||||
            android:layout_height="wrap_content"
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
            android:orientation="vertical"
 | 
					            android:orientation="vertical"
 | 
				
			||||||
            android:padding="24dp"
 | 
					            android:padding="32dp"
 | 
				
			||||||
            android:gravity="center_horizontal">
 | 
					            android:gravity="center">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Generate QR Title -->
 | 
					            <!-- QRIS Logo -->
 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:text="Generate QR"
 | 
					 | 
				
			||||||
                android:textColor="@android:color/black"
 | 
					 | 
				
			||||||
                android:textSize="18sp"
 | 
					 | 
				
			||||||
                android:textStyle="bold"
 | 
					 | 
				
			||||||
                android:layout_marginBottom="24dp" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- QRIS Logo Text -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					            <TextView
 | 
				
			||||||
 | 
					                android:id="@+id/qris_logo"
 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
                android:text="QRIS"
 | 
					                android:text="QRIS"
 | 
				
			||||||
                android:textColor="@android:color/black"
 | 
					 | 
				
			||||||
                android:textSize="32sp"
 | 
					                android:textSize="32sp"
 | 
				
			||||||
                android:textStyle="bold"
 | 
					                android:textStyle="bold"
 | 
				
			||||||
                android:fontFamily="monospace"
 | 
					                android:textColor="#000000"
 | 
				
			||||||
                android:layout_marginBottom="24dp" />
 | 
					                android:letterSpacing="0.1"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="24dp"
 | 
				
			||||||
 | 
					                android:fontFamily="@font/inter" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- QR Code -->
 | 
					            <!-- QR Code -->
 | 
				
			||||||
            <ImageView
 | 
					            <ImageView
 | 
				
			||||||
                android:id="@+id/qrImageView"
 | 
					                android:id="@+id/qrImageView"
 | 
				
			||||||
                android:layout_width="200dp"
 | 
					                android:layout_width="240dp"
 | 
				
			||||||
                android:layout_height="200dp"
 | 
					                android:layout_height="240dp"
 | 
				
			||||||
                android:layout_gravity="center_horizontal"
 | 
					                android:layout_marginBottom="24dp"
 | 
				
			||||||
                android:contentDescription="QRIS QR Code"
 | 
					                android:scaleType="centerInside"
 | 
				
			||||||
                android:scaleType="fitCenter"
 | 
					                android:background="#FFFFFF"
 | 
				
			||||||
                android:background="#F0F0F0"
 | 
					                android:padding="8dp" />
 | 
				
			||||||
                android:layout_marginBottom="24dp" />
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Amount Display -->
 | 
					            <!-- Amount -->
 | 
				
			||||||
            <TextView
 | 
					            <TextView
 | 
				
			||||||
                android:id="@+id/amountTextView"
 | 
					                android:id="@+id/amountTextView"
 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					                android:layout_width="wrap_content"
 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="wrap_content"
 | 
				
			||||||
                android:text="RP.200.000"
 | 
					                android:text="RP.200.000"
 | 
				
			||||||
                android:textColor="@android:color/black"
 | 
					                android:textSize="24sp"
 | 
				
			||||||
                android:textSize="20sp"
 | 
					 | 
				
			||||||
                android:textStyle="bold"
 | 
					                android:textStyle="bold"
 | 
				
			||||||
                android:layout_marginBottom="16dp" />
 | 
					                android:textColor="#333333"
 | 
				
			||||||
 | 
					                android:layout_marginBottom="16dp"
 | 
				
			||||||
 | 
					                android:fontFamily="@font/inter" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Timer/Counter -->
 | 
					            <!-- Timer -->
 | 
				
			||||||
            <TextView
 | 
					            <TextView
 | 
				
			||||||
                android:id="@+id/timerTextView"
 | 
					                android:id="@+id/timerTextView"
 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					                android:layout_width="48dp"
 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					                android:layout_height="48dp"
 | 
				
			||||||
                android:text="60"
 | 
					                android:text="60"
 | 
				
			||||||
                android:textColor="#E31937"
 | 
					 | 
				
			||||||
                android:textSize="18sp"
 | 
					                android:textSize="18sp"
 | 
				
			||||||
                android:textStyle="bold"
 | 
					                android:textStyle="bold"
 | 
				
			||||||
                android:layout_marginBottom="24dp" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <!-- QR Refresh Status -->
 | 
					 | 
				
			||||||
            <TextView
 | 
					 | 
				
			||||||
                android:id="@+id/qrStatusTextView"
 | 
					 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:text="QR Code akan refresh dalam"
 | 
					 | 
				
			||||||
                android:textColor="#666666"
 | 
					                android:textColor="#666666"
 | 
				
			||||||
                android:textSize="12sp"
 | 
					                android:gravity="center"
 | 
				
			||||||
                android:layout_marginBottom="16dp"
 | 
					                android:background="@drawable/timer_circle_background"
 | 
				
			||||||
                android:visibility="visible" />
 | 
					                android:fontFamily="@font/inter" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Action Buttons Section -->
 | 
					 | 
				
			||||||
            <LinearLayout
 | 
					 | 
				
			||||||
                android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:orientation="vertical"
 | 
					 | 
				
			||||||
                android:layout_marginTop="16dp">
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- Download QRIS Button -->
 | 
					 | 
				
			||||||
                <Button
 | 
					 | 
				
			||||||
                    android:id="@+id/downloadQrisButton"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="48dp"
 | 
					 | 
				
			||||||
                    android:layout_marginBottom="8dp"
 | 
					 | 
				
			||||||
                    android:text="Download QRIS"
 | 
					 | 
				
			||||||
                    android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                    android:textSize="14sp"
 | 
					 | 
				
			||||||
                    android:background="#4CAF50"
 | 
					 | 
				
			||||||
                    android:visibility="visible" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- Check Payment Status Button -->
 | 
					 | 
				
			||||||
                <Button
 | 
					 | 
				
			||||||
                    android:id="@+id/checkStatusButton"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="48dp"
 | 
					 | 
				
			||||||
                    android:layout_marginBottom="8dp"
 | 
					 | 
				
			||||||
                    android:text="Check Payment Status"
 | 
					 | 
				
			||||||
                    android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                    android:textSize="14sp"
 | 
					 | 
				
			||||||
                    android:background="#2196F3"
 | 
					 | 
				
			||||||
                    android:visibility="visible" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                <!-- Return to Main Button -->
 | 
					 | 
				
			||||||
                <Button
 | 
					 | 
				
			||||||
                    android:id="@+id/returnMainButton"
 | 
					 | 
				
			||||||
                    android:layout_width="match_parent"
 | 
					 | 
				
			||||||
                    android:layout_height="48dp"
 | 
					 | 
				
			||||||
                    android:text="Return to Main"
 | 
					 | 
				
			||||||
                    android:textColor="@android:color/white"
 | 
					 | 
				
			||||||
                    android:textSize="14sp"
 | 
					 | 
				
			||||||
                    android:background="#FF9800"
 | 
					 | 
				
			||||||
                    android:visibility="visible" />
 | 
					 | 
				
			||||||
        </LinearLayout>
 | 
					        </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <!-- Hidden views for compatibility -->
 | 
					    </androidx.cardview.widget.CardView>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Cancel Button -->
 | 
				
			||||||
 | 
					    <Button
 | 
				
			||||||
 | 
					        android:id="@+id/cancel_button"
 | 
				
			||||||
 | 
					        android:layout_width="0dp"
 | 
				
			||||||
 | 
					        android:layout_height="52dp"
 | 
				
			||||||
 | 
					        android:layout_marginStart="16dp"
 | 
				
			||||||
 | 
					        android:layout_marginEnd="16dp"
 | 
				
			||||||
 | 
					        android:layout_marginBottom="32dp"
 | 
				
			||||||
 | 
					        android:text="Batalkan"
 | 
				
			||||||
 | 
					        android:textColor="#E31937"
 | 
				
			||||||
 | 
					        android:textSize="16sp"
 | 
				
			||||||
 | 
					        android:textStyle="bold"
 | 
				
			||||||
 | 
					        android:background="@drawable/button_cancel_background"
 | 
				
			||||||
 | 
					        android:fontFamily="@font/inter"
 | 
				
			||||||
 | 
					        android:textAllCaps="false"
 | 
				
			||||||
 | 
					        app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintEnd_toEndOf="parent" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Hidden Elements for Functionality -->
 | 
				
			||||||
    <TextView
 | 
					    <TextView
 | 
				
			||||||
        android:id="@+id/referenceTextView"
 | 
					        android:id="@+id/referenceTextView"
 | 
				
			||||||
        android:layout_width="wrap_content"
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
                android:text="Reference ID: ref-12345"
 | 
					        android:visibility="gone"
 | 
				
			||||||
                android:textColor="@android:color/black"
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
                android:textSize="14sp"
 | 
					        app:layout_constraintStart_toStartOf="parent" />
 | 
				
			||||||
                android:visibility="gone" />
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            <ProgressBar
 | 
					 | 
				
			||||||
                android:id="@+id/progressBar"
 | 
					 | 
				
			||||||
                android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                android:visibility="gone" />
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <TextView
 | 
					    <TextView
 | 
				
			||||||
        android:id="@+id/statusTextView"
 | 
					        android:id="@+id/statusTextView"
 | 
				
			||||||
        android:layout_width="wrap_content"
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
        android:layout_height="wrap_content"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
                android:text="Payment Status Success"
 | 
					        android:visibility="gone"
 | 
				
			||||||
                android:textColor="@android:color/black"
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
                android:textSize="18sp"
 | 
					        app:layout_constraintStart_toStartOf="parent" />
 | 
				
			||||||
                android:visibility="gone" />
 | 
					 | 
				
			||||||
        </LinearLayout>
 | 
					 | 
				
			||||||
    </androidx.cardview.widget.CardView>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Spacer to push button to bottom -->
 | 
					    <TextView
 | 
				
			||||||
    <View
 | 
					        android:id="@+id/qrStatusTextView"
 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
        android:layout_height="0dp"
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
        android:layout_weight="1" />
 | 
					        android:visibility="gone"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <ProgressBar
 | 
				
			||||||
 | 
					        android:id="@+id/progressBar"
 | 
				
			||||||
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:visibility="gone"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Bottom Cancel Button -->
 | 
					 | 
				
			||||||
    <Button
 | 
					    <Button
 | 
				
			||||||
        android:id="@+id/cancelButton"
 | 
					        android:id="@+id/downloadQrisButton"
 | 
				
			||||||
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:visibility="gone"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Button
 | 
				
			||||||
 | 
					        android:id="@+id/checkStatusButton"
 | 
				
			||||||
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:visibility="gone"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <Button
 | 
				
			||||||
 | 
					        android:id="@+id/returnMainButton"
 | 
				
			||||||
 | 
					        android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					        android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					        android:visibility="gone"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Success Screen (Full Screen Overlay) -->
 | 
				
			||||||
 | 
					    <LinearLayout
 | 
				
			||||||
 | 
					        android:id="@+id/success_screen"
 | 
				
			||||||
        android:layout_width="match_parent"
 | 
					        android:layout_width="match_parent"
 | 
				
			||||||
        android:layout_height="48dp"
 | 
					        android:layout_height="match_parent"
 | 
				
			||||||
        android:layout_margin="16dp"
 | 
					        android:orientation="vertical"
 | 
				
			||||||
        android:text="Batalkan"
 | 
					        android:gravity="center"
 | 
				
			||||||
        android:textColor="#E31937"
 | 
					        android:background="#E31937"
 | 
				
			||||||
        android:textSize="16sp"
 | 
					        android:visibility="gone"
 | 
				
			||||||
        android:textStyle="normal"
 | 
					        app:layout_constraintTop_toTopOf="parent"
 | 
				
			||||||
        android:background="@android:color/transparent"
 | 
					        app:layout_constraintBottom_toBottomOf="parent"
 | 
				
			||||||
        style="?android:attr/borderlessButtonStyle" />
 | 
					        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>
 | 
					    </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</androidx.constraintlayout.widget.ConstraintLayout>
 | 
				
			||||||
@ -104,7 +104,7 @@
 | 
				
			|||||||
                        <TextView
 | 
					                        <TextView
 | 
				
			||||||
                            android:layout_width="wrap_content"
 | 
					                            android:layout_width="wrap_content"
 | 
				
			||||||
                            android:layout_height="wrap_content"
 | 
					                            android:layout_height="wrap_content"
 | 
				
			||||||
                            android:text="EDC "
 | 
					                            android:text="Payvora "
 | 
				
			||||||
                            android:textColor="#E31937"
 | 
					                            android:textColor="#E31937"
 | 
				
			||||||
                            android:textSize="24sp"
 | 
					                            android:textSize="24sp"
 | 
				
			||||||
                            android:textStyle="bold"
 | 
					                            android:textStyle="bold"
 | 
				
			||||||
@ -113,7 +113,7 @@
 | 
				
			|||||||
                        <TextView
 | 
					                        <TextView
 | 
				
			||||||
                            android:layout_width="wrap_content"
 | 
					                            android:layout_width="wrap_content"
 | 
				
			||||||
                            android:layout_height="wrap_content"
 | 
					                            android:layout_height="wrap_content"
 | 
				
			||||||
                            android:text="Merchant"
 | 
					                            android:text="PRO"
 | 
				
			||||||
                            android:textColor="#3F51B5"
 | 
					                            android:textColor="#3F51B5"
 | 
				
			||||||
                            android:textSize="24sp"
 | 
					                            android:textSize="24sp"
 | 
				
			||||||
                            android:textStyle="bold"
 | 
					                            android:textStyle="bold"
 | 
				
			||||||
@ -121,15 +121,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    </LinearLayout>
 | 
					                    </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:layout_width="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:text="BANK BRI"
 | 
					 | 
				
			||||||
                        android:textColor="#333333"
 | 
					 | 
				
			||||||
                        android:textSize="12sp"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/inter"
 | 
					 | 
				
			||||||
                        android:layout_marginTop="2dp"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					                </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <!-- Merchant Info -->
 | 
					                <!-- Merchant Info -->
 | 
				
			||||||
@ -168,46 +159,36 @@
 | 
				
			|||||||
                    android:layout_width="match_parent"
 | 
					                    android:layout_width="match_parent"
 | 
				
			||||||
                    android:layout_height="wrap_content"
 | 
					                    android:layout_height="wrap_content"
 | 
				
			||||||
                    android:orientation="horizontal"
 | 
					                    android:orientation="horizontal"
 | 
				
			||||||
 | 
					                    android:gravity="center"
 | 
				
			||||||
                    android:layout_marginBottom="8dp">
 | 
					                    android:layout_marginBottom="8dp">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <TextView
 | 
					 | 
				
			||||||
                        android:layout_width="0dp"
 | 
					 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					 | 
				
			||||||
                        android:layout_weight="1"
 | 
					 | 
				
			||||||
                        android:text="MID: "
 | 
					 | 
				
			||||||
                        android:textColor="#666666"
 | 
					 | 
				
			||||||
                        android:textSize="12sp"
 | 
					 | 
				
			||||||
                        android:fontFamily="@font/inter"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                    <TextView
 | 
					                    <TextView
 | 
				
			||||||
                        android:id="@+id/mid_text"
 | 
					                        android:id="@+id/mid_text"
 | 
				
			||||||
                        android:layout_width="0dp"
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
                        android:layout_weight="1"
 | 
					                        android:text="MID:12345678901"
 | 
				
			||||||
                        android:text="1234567890"
 | 
					 | 
				
			||||||
                        android:textColor="#333333"
 | 
					                        android:textColor="#333333"
 | 
				
			||||||
                        android:textSize="12sp"
 | 
					                        android:textSize="12sp"
 | 
				
			||||||
                        android:fontFamily="@font/inter"/>
 | 
					                        android:fontFamily="@font/inter"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    <!-- Vertical Separator -->
 | 
				
			||||||
                    <TextView
 | 
					                    <TextView
 | 
				
			||||||
                        android:layout_width="0dp"
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
                        android:layout_weight="1"
 | 
					                        android:text="  |  "
 | 
				
			||||||
                        android:text="TID: "
 | 
					                        android:textColor="#999999"
 | 
				
			||||||
                        android:textColor="#666666"
 | 
					 | 
				
			||||||
                        android:textSize="12sp"
 | 
					                        android:textSize="12sp"
 | 
				
			||||||
                        android:fontFamily="@font/inter"/>
 | 
					                        android:fontFamily="@font/inter"
 | 
				
			||||||
 | 
					                        android:paddingHorizontal="8dp"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    <TextView
 | 
					                    <TextView
 | 
				
			||||||
                        android:id="@+id/tid_text"
 | 
					                        android:id="@+id/tid_text"
 | 
				
			||||||
                        android:layout_width="0dp"
 | 
					                        android:layout_width="wrap_content"
 | 
				
			||||||
                        android:layout_height="wrap_content"
 | 
					                        android:layout_height="wrap_content"
 | 
				
			||||||
                        android:layout_weight="1"
 | 
					                        android:text="TID:12345678901"
 | 
				
			||||||
                        android:text="1234567890"
 | 
					 | 
				
			||||||
                        android:textColor="#333333"
 | 
					                        android:textColor="#333333"
 | 
				
			||||||
                        android:textSize="12sp"
 | 
					                        android:textSize="12sp"
 | 
				
			||||||
                        android:fontFamily="@font/inter"/>
 | 
					                        android:fontFamily="@font/inter"/>
 | 
				
			||||||
 | 
					 | 
				
			||||||
                </LinearLayout>
 | 
					                </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <!-- Separator Line -->
 | 
					                <!-- Separator Line -->
 | 
				
			||||||
 | 
				
			|||||||
@ -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"
 | 
				
			||||||
							
								
								
									
										47
									
								
								app/src/main/res/layout/component_appbar.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								app/src/main/res/layout/component_appbar.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,47 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<androidx.constraintlayout.widget.ConstraintLayout 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="130dp"
 | 
				
			||||||
 | 
					    android:background="#DE0701"
 | 
				
			||||||
 | 
					    android:fitsSystemWindows="true">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 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_marginTop="16dp"
 | 
				
			||||||
 | 
					        android:background="?attr/selectableItemBackgroundBorderless"
 | 
				
			||||||
 | 
					        android:padding="4dp"
 | 
				
			||||||
 | 
					        app:layout_constraintStart_toStartOf="parent"
 | 
				
			||||||
 | 
					        app:layout_constraintTop_toTopOf="parent">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- Back Arrow -->
 | 
				
			||||||
 | 
					        <ImageView
 | 
				
			||||||
 | 
					            android:id="@+id/backArrow"
 | 
				
			||||||
 | 
					            android:layout_width="16dp"
 | 
				
			||||||
 | 
					            android:layout_height="16dp"
 | 
				
			||||||
 | 
					            android:src="@drawable/ic_arrow_back"
 | 
				
			||||||
 | 
					            android:contentDescription="Back"
 | 
				
			||||||
 | 
					            app:tint="@android:color/white" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- Title Text -->
 | 
				
			||||||
 | 
					        <TextView
 | 
				
			||||||
 | 
					            android:id="@+id/appbarTitle"
 | 
				
			||||||
 | 
					            android:layout_width="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_height="wrap_content"
 | 
				
			||||||
 | 
					            android:layout_marginStart="2dp"
 | 
				
			||||||
 | 
					            android:text="Kembali"
 | 
				
			||||||
 | 
					            android:textColor="@android:color/white"
 | 
				
			||||||
 | 
					            android:textSize="10sp"
 | 
				
			||||||
 | 
					            android:fontFamily="@font/inter"
 | 
				
			||||||
 | 
					            android:textStyle="normal" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    </LinearLayout>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</androidx.constraintlayout.widget.ConstraintLayout>
 | 
				
			||||||
@ -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>
 | 
				
			||||||
@ -1,9 +1,28 @@
 | 
				
			|||||||
<?xml version="1.0" encoding="utf-8"?>
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
<resources>
 | 
					<resources>
 | 
				
			||||||
    <color name="black">#FF000000</color>
 | 
					    <color name="colorPrimary">#3F51B5</color>
 | 
				
			||||||
    <color name="white">#FFFFFFFF</color>
 | 
					    <color name="colorPrimaryDark">#303F9F</color>
 | 
				
			||||||
 | 
					    <color name="colorAccent">#FF4081</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <color name="white">#FFFFFF</color>
 | 
				
			||||||
 | 
					    <color name="colorOrange">#FF6600</color>
 | 
				
			||||||
 | 
					    <color name="transparent">#00000000</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <color name="colorBackground">#F0F2F5</color>
 | 
				
			||||||
 | 
					    <color name="colorTextTitle">#222222</color>
 | 
				
			||||||
 | 
					    <color name="colorTextContent">#666666</color>
 | 
				
			||||||
 | 
					    <color name="colorTextHelp">#999999</color>
 | 
				
			||||||
 | 
					    <color name="colorLineColor">#d7d7d7</color>
 | 
				
			||||||
 | 
					    <color name="FD5A52">#FD5A52</color>
 | 
				
			||||||
 | 
					    <color name="CE6E6E6">#E6E6E6</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <color name="FF3C00">#FF3C00</color>
 | 
				
			||||||
 | 
					    <color name="C999999">#999999</color>
 | 
				
			||||||
 | 
					    <color name="black">#000000</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    <!-- Banking App Theme Colors -->
 | 
					    <!-- Banking App Theme Colors -->
 | 
				
			||||||
 | 
					    <color name="red">#DE0701</color>
 | 
				
			||||||
    <color name="primary_blue">#1976D2</color>
 | 
					    <color name="primary_blue">#1976D2</color>
 | 
				
			||||||
    <color name="light_blue">#BBDEFB</color>
 | 
					    <color name="light_blue">#BBDEFB</color>
 | 
				
			||||||
    <color name="accent_teal">#009688</color>
 | 
					    <color name="accent_teal">#009688</color>
 | 
				
			||||||
@ -13,4 +32,88 @@
 | 
				
			|||||||
    <color name="light_gray">#F5F5F5</color>
 | 
					    <color name="light_gray">#F5F5F5</color>
 | 
				
			||||||
    <color name="medium_gray">#E0E0E0</color>
 | 
					    <color name="medium_gray">#E0E0E0</color>
 | 
				
			||||||
    <color name="dark_gray">#757575</color>
 | 
					    <color name="dark_gray">#757575</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Alias untuk kemudahan penggunaan dalam Clean Architecture -->
 | 
				
			||||||
 | 
					    <!-- Menggunakan warna yang sudah ada -->
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    <!-- Button Colors -->
 | 
				
			||||||
 | 
					    <color name="button_primary">@color/primary_blue</color>
 | 
				
			||||||
 | 
					    <color name="button_primary_pressed">@color/colorPrimaryDark</color>
 | 
				
			||||||
 | 
					    <color name="button_secondary">@color/white</color>
 | 
				
			||||||
 | 
					    <color name="button_outline_text">@color/primary_blue</color>
 | 
				
			||||||
 | 
					    <color name="button_disabled">@color/medium_gray</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Background Colors -->
 | 
				
			||||||
 | 
					    <color name="background_main">@color/colorBackground</color>
 | 
				
			||||||
 | 
					    <color name="background_card">@color/white</color>
 | 
				
			||||||
 | 
					    <color name="background_surface">@color/white</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Text Colors -->
 | 
				
			||||||
 | 
					    <color name="text_primary">@color/colorTextTitle</color>
 | 
				
			||||||
 | 
					    <color name="text_secondary">@color/colorTextContent</color>
 | 
				
			||||||
 | 
					    <color name="text_hint">@color/colorTextHelp</color>
 | 
				
			||||||
 | 
					    <color name="text_disabled">@color/C999999</color>
 | 
				
			||||||
 | 
					    <color name="text_on_primary">@color/white</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Status Colors -->
 | 
				
			||||||
 | 
					    <color name="status_success">@color/accent_green</color>
 | 
				
			||||||
 | 
					    <color name="status_error">@color/FD5A52</color>
 | 
				
			||||||
 | 
					    <color name="status_warning">@color/colorOrange</color>
 | 
				
			||||||
 | 
					    <color name="status_info">@color/light_blue</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- EMV Processing Colors -->
 | 
				
			||||||
 | 
					    <color name="emv_processing">@color/colorOrange</color>
 | 
				
			||||||
 | 
					    <color name="emv_success">@color/accent_green</color>
 | 
				
			||||||
 | 
					    <color name="emv_error">@color/FD5A52</color>
 | 
				
			||||||
 | 
					    <color name="emv_idle">@color/dark_gray</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Card Colors -->
 | 
				
			||||||
 | 
					    <color name="card_background">@color/white</color>
 | 
				
			||||||
 | 
					    <color name="card_shadow">@color/medium_gray</color>
 | 
				
			||||||
 | 
					    <color name="card_border">@color/colorLineColor</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Amount Display Colors -->
 | 
				
			||||||
 | 
					    <color name="amount_primary">@color/primary_blue</color>
 | 
				
			||||||
 | 
					    <color name="amount_background">@color/light_blue</color>
 | 
				
			||||||
 | 
					    <color name="amount_success">@color/accent_green</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Border and Divider Colors -->
 | 
				
			||||||
 | 
					    <color name="border_light">@color/colorLineColor</color>
 | 
				
			||||||
 | 
					    <color name="border_medium">@color/medium_gray</color>
 | 
				
			||||||
 | 
					    <color name="border_dark">@color/dark_gray</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Overlay and Shadow Colors -->
 | 
				
			||||||
 | 
					    <color name="overlay_light">@color/transparent</color>
 | 
				
			||||||
 | 
					    <color name="overlay_dark">@color/transparent</color>
 | 
				
			||||||
 | 
					    <color name="shadow_color">@color/medium_gray</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Transaction Flow Colors -->
 | 
				
			||||||
 | 
					    <color name="transaction_amount">@color/primary_blue</color>
 | 
				
			||||||
 | 
					    <color name="transaction_success">@color/accent_green</color>
 | 
				
			||||||
 | 
					    <color name="transaction_processing">@color/colorOrange</color>
 | 
				
			||||||
 | 
					    <color name="transaction_failed">@color/FD5A52</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Keypad Colors -->
 | 
				
			||||||
 | 
					    <color name="keypad_button_text">@color/colorTextTitle</color>
 | 
				
			||||||
 | 
					    <color name="keypad_button_background">@color/white</color>
 | 
				
			||||||
 | 
					    <color name="keypad_button_pressed">@color/light_gray</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- PIN Pad Colors -->
 | 
				
			||||||
 | 
					    <color name="pinpad_background">@color/primary_blue</color>
 | 
				
			||||||
 | 
					    <color name="pinpad_text">@color/white</color>
 | 
				
			||||||
 | 
					    <color name="pinpad_button">@color/white</color>
 | 
				
			||||||
 | 
					    <color name="pinpad_button_pressed">@color/light_gray</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Navigation Colors -->
 | 
				
			||||||
 | 
					    <color name="toolbar_background">@color/primary_blue</color>
 | 
				
			||||||
 | 
					    <color name="toolbar_text">@color/white</color>
 | 
				
			||||||
 | 
					    <color name="navigation_background">@color/white</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Icon Colors -->
 | 
				
			||||||
 | 
					    <color name="icon_primary">@color/primary_blue</color>
 | 
				
			||||||
 | 
					    <color name="icon_secondary">@color/dark_gray</color>
 | 
				
			||||||
 | 
					    <color name="icon_success">@color/accent_green</color>
 | 
				
			||||||
 | 
					    <color name="icon_error">@color/FD5A52</color>
 | 
				
			||||||
 | 
					    <color name="icon_on_primary">@color/white</color>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</resources>
 | 
					</resources>
 | 
				
			||||||
							
								
								
									
										8
									
								
								app/src/main/res/values/dimens.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								app/src/main/res/values/dimens.xml
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<resources>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <dimen name="smallSize">24dp</dimen>
 | 
				
			||||||
 | 
					    <dimen name="itemSize">48dp</dimen>
 | 
				
			||||||
 | 
					    <dimen name="titleSize">56dp</dimen>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</resources>
 | 
				
			||||||
@ -13,4 +13,11 @@
 | 
				
			|||||||
    <string name="payment_status_success">Payment Successful!</string>
 | 
					    <string name="payment_status_success">Payment Successful!</string>
 | 
				
			||||||
    <string name="return_main">Return to Main Screen</string>
 | 
					    <string name="return_main">Return to Main Screen</string>
 | 
				
			||||||
    <string name="main_title">POC</string>
 | 
					    <string name="main_title">POC</string>
 | 
				
			||||||
 | 
					    <!-- In res/values/strings.xml -->    
 | 
				
			||||||
 | 
					    <string name="card_test_credit_card">Credit Card Test</string>
 | 
				
			||||||
 | 
					    <string name="card_mag_card_detected">Magnetic Card Detected</string>
 | 
				
			||||||
 | 
					    <string name="card_ic_card_detected">IC Card Detected</string>
 | 
				
			||||||
 | 
					    <string name="card_start_check_card">Start Check Card</string>
 | 
				
			||||||
 | 
					    <string name="card_stop_check_card">Stop Check Card</string>
 | 
				
			||||||
 | 
					    <string name="connect_fail">Connection failed</string>
 | 
				
			||||||
</resources>
 | 
					</resources>
 | 
				
			||||||
@ -29,6 +29,11 @@
 | 
				
			|||||||
        <item name="titleTextAppearance">@style/ToolbarTitleStyle</item>
 | 
					        <item name="titleTextAppearance">@style/ToolbarTitleStyle</item>
 | 
				
			||||||
    </style>
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <style name="Toolbar.TitleText" parent="TextAppearance.Widget.AppCompat.Toolbar.Title">
 | 
				
			||||||
 | 
					        <item name="android:textSize">18sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@android:color/white</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <!-- Numpad Button Style -->
 | 
					    <!-- Numpad Button Style -->
 | 
				
			||||||
    <style name="NumpadButton">
 | 
					    <style name="NumpadButton">
 | 
				
			||||||
        <item name="android:layout_width">0dp</item>
 | 
					        <item name="android:layout_width">0dp</item>
 | 
				
			||||||
@ -45,4 +50,172 @@
 | 
				
			|||||||
        <item name="android:focusable">true</item>
 | 
					        <item name="android:focusable">true</item>
 | 
				
			||||||
    </style>
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Additional Styles for Clean Architecture -->
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    <!-- Keypad Button Styles untuk CreateTransaction -->
 | 
				
			||||||
 | 
					    <style name="KeypadButton">
 | 
				
			||||||
 | 
					        <item name="android:layout_width">0dp</item>
 | 
				
			||||||
 | 
					        <item name="android:layout_height">56dp</item>
 | 
				
			||||||
 | 
					        <item name="android:layout_columnWeight">1</item>
 | 
				
			||||||
 | 
					        <item name="android:layout_margin">4dp</item>
 | 
				
			||||||
 | 
					        <item name="android:background">?attr/selectableItemBackground</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/colorTextTitle</item>
 | 
				
			||||||
 | 
					        <item name="android:textSize">20sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textStyle">bold</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					        <item name="android:gravity">center</item>
 | 
				
			||||||
 | 
					        <item name="android:clickable">true</item>
 | 
				
			||||||
 | 
					        <item name="android:focusable">true</item>
 | 
				
			||||||
 | 
					        <item name="android:elevation">2dp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <style name="KeypadButtonSecondary" parent="KeypadButton">
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/dark_gray</item>
 | 
				
			||||||
 | 
					        <item name="android:textSize">18sp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Button Styles -->
 | 
				
			||||||
 | 
					    <style name="PrimaryButton">
 | 
				
			||||||
 | 
					        <item name="android:background">@color/primary_blue</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/white</item>
 | 
				
			||||||
 | 
					        <item name="android:textStyle">bold</item>
 | 
				
			||||||
 | 
					        <item name="android:textSize">16sp</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					        <item name="android:elevation">4dp</item>
 | 
				
			||||||
 | 
					        <item name="android:clickable">true</item>
 | 
				
			||||||
 | 
					        <item name="android:focusable">true</item>
 | 
				
			||||||
 | 
					        <item name="android:padding">12dp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <style name="SecondaryButton">
 | 
				
			||||||
 | 
					        <item name="android:background">@color/white</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/primary_blue</item>
 | 
				
			||||||
 | 
					        <item name="android:textStyle">bold</item>
 | 
				
			||||||
 | 
					        <item name="android:textSize">16sp</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					        <item name="android:clickable">true</item>
 | 
				
			||||||
 | 
					        <item name="android:focusable">true</item>
 | 
				
			||||||
 | 
					        <item name="android:padding">12dp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <style name="OutlineButton">
 | 
				
			||||||
 | 
					        <item name="android:background">?attr/selectableItemBackground</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/primary_blue</item>
 | 
				
			||||||
 | 
					        <item name="android:textStyle">bold</item>
 | 
				
			||||||
 | 
					        <item name="android:textSize">16sp</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					        <item name="android:clickable">true</item>
 | 
				
			||||||
 | 
					        <item name="android:focusable">true</item>
 | 
				
			||||||
 | 
					        <item name="android:padding">12dp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Text Styles -->
 | 
				
			||||||
 | 
					    <style name="HeaderTextStyle">
 | 
				
			||||||
 | 
					        <item name="android:textSize">20sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textStyle">bold</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/colorTextTitle</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					        <item name="android:gravity">center</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <style name="SubHeaderTextStyle">
 | 
				
			||||||
 | 
					        <item name="android:textSize">16sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textStyle">bold</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/colorTextContent</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <style name="BodyTextStyle">
 | 
				
			||||||
 | 
					        <item name="android:textSize">14sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/colorTextTitle</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					        <item name="android:lineSpacingExtra">2dp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <style name="MonospaceTextStyle" parent="BodyTextStyle">
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">monospace</item>
 | 
				
			||||||
 | 
					        <item name="android:textSize">12sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/colorTextContent</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <style name="HintTextStyle">
 | 
				
			||||||
 | 
					        <item name="android:textSize">14sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/colorTextHelp</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					        <item name="android:lineSpacingExtra">2dp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Amount Display Style -->
 | 
				
			||||||
 | 
					    <style name="AmountDisplayStyle">
 | 
				
			||||||
 | 
					        <item name="android:textSize">32sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textStyle">bold</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/primary_blue</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					        <item name="android:gravity">center</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Status Text Style -->
 | 
				
			||||||
 | 
					    <style name="StatusTextStyle">
 | 
				
			||||||
 | 
					        <item name="android:textSize">16sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/colorTextTitle</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					        <item name="android:gravity">center</item>
 | 
				
			||||||
 | 
					        <item name="android:lineSpacingExtra">4dp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Success Text Style -->
 | 
				
			||||||
 | 
					    <style name="SuccessTextStyle">
 | 
				
			||||||
 | 
					        <item name="android:textSize">20sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textStyle">bold</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/white</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					        <item name="android:gravity">center</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Card Title Style -->
 | 
				
			||||||
 | 
					    <style name="CardTitleStyle">
 | 
				
			||||||
 | 
					        <item name="android:textSize">18sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textStyle">bold</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/colorTextTitle</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					        <item name="android:layout_marginBottom">16dp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Mode Indicator Style -->
 | 
				
			||||||
 | 
					    <style name="ModeIndicatorStyle">
 | 
				
			||||||
 | 
					        <item name="android:textSize">14sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/colorTextContent</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">@font/inter</item>
 | 
				
			||||||
 | 
					        <item name="android:gravity">center</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Transaction Summary Style -->
 | 
				
			||||||
 | 
					    <style name="TransactionSummaryStyle">
 | 
				
			||||||
 | 
					        <item name="android:textSize">14sp</item>
 | 
				
			||||||
 | 
					        <item name="android:textColor">@color/colorTextContent</item>
 | 
				
			||||||
 | 
					        <item name="android:fontFamily">monospace</item>
 | 
				
			||||||
 | 
					        <item name="android:lineSpacingExtra">2dp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Amount Card Style -->
 | 
				
			||||||
 | 
					    <style name="AmountCardStyle">
 | 
				
			||||||
 | 
					        <item name="android:background">@color/primary_blue</item>
 | 
				
			||||||
 | 
					        <item name="android:padding">20dp</item>
 | 
				
			||||||
 | 
					        <item name="android:elevation">4dp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Success Card Style -->
 | 
				
			||||||
 | 
					    <style name="SuccessCardStyle">
 | 
				
			||||||
 | 
					        <item name="android:background">@color/accent_green</item>
 | 
				
			||||||
 | 
					        <item name="android:padding">20dp</item>
 | 
				
			||||||
 | 
					        <item name="android:elevation">4dp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- Data Card Style -->
 | 
				
			||||||
 | 
					    <style name="DataCardStyle">
 | 
				
			||||||
 | 
					        <item name="android:background">@color/white</item>
 | 
				
			||||||
 | 
					        <item name="android:padding">20dp</item>
 | 
				
			||||||
 | 
					        <item name="android:elevation">4dp</item>
 | 
				
			||||||
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
</resources>
 | 
					</resources>
 | 
				
			||||||
@ -1,8 +1,9 @@
 | 
				
			|||||||
<resources xmlns:tools="http://schemas.android.com/tools">
 | 
					<resources xmlns:tools="http://schemas.android.com/tools">
 | 
				
			||||||
    <!-- Base application theme. -->
 | 
					    <!-- Base application theme. -->
 | 
				
			||||||
    <style name="Base.Theme.BDKIPOC" parent="Theme.Material3.DayNight.NoActionBar">
 | 
					    <style name="Base.Theme.BDKIPOC" parent="Theme.Material3.DayNight.NoActionBar">
 | 
				
			||||||
        <!-- Customize your light theme here. -->
 | 
					        <!-- Customize your theme here. -->
 | 
				
			||||||
        <!-- <item name="colorPrimary">@color/my_light_primary</item> -->
 | 
					        <item name="colorPrimary">@color/red</item>
 | 
				
			||||||
 | 
					        <item name="colorOnPrimary">@color/white</item>
 | 
				
			||||||
    </style>
 | 
					    </style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    <style name="Theme.BDKIPOC" parent="Base.Theme.BDKIPOC" />
 | 
					    <style name="Theme.BDKIPOC" parent="Base.Theme.BDKIPOC" />
 | 
				
			||||||
 | 
				
			|||||||
@ -16,6 +16,14 @@ dependencyResolutionManagement {
 | 
				
			|||||||
    repositories {
 | 
					    repositories {
 | 
				
			||||||
        google()
 | 
					        google()
 | 
				
			||||||
        mavenCentral()
 | 
					        mavenCentral()
 | 
				
			||||||
 | 
					        // Tambahkan repositories Sunmi
 | 
				
			||||||
 | 
					        maven { url "https://s01.oss.sonatype.org/content/repositories/snapshots" }
 | 
				
			||||||
 | 
					        maven { url "https://s01.oss.sonatype.org/content/groups/public/" }
 | 
				
			||||||
 | 
					        maven { url "https://jcenter.bintray.com" }
 | 
				
			||||||
 | 
					        maven { url "https://repo.spring.io/libs-milestone" }
 | 
				
			||||||
 | 
					        flatDir {
 | 
				
			||||||
 | 
					            dirs 'app/libs'
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user