diff --git a/app/src/main/java/com/example/bdkipoc/kredit/EmvTransactionActivity.java b/app/src/main/java/com/example/bdkipoc/kredit/EmvTransactionActivity.java
index f9b74d6..7922332 100644
--- a/app/src/main/java/com/example/bdkipoc/kredit/EmvTransactionActivity.java
+++ b/app/src/main/java/com/example/bdkipoc/kredit/EmvTransactionActivity.java
@@ -9,7 +9,11 @@ import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
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.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
@@ -42,20 +46,29 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
- * EmvTransactionActivity - Handle card reading and EMV processing
+ * EmvTransactionActivity - Handle card reading and EMV processing with Modal
*/
public class EmvTransactionActivity extends AppCompatActivity {
private static final String TAG = "EmvTransaction";
- // UI Components - SIMPLIFIED
+ // UI Components - SIMPLIFIED + MODAL
+ private LinearLayout mainContent;
+ private FrameLayout modalOverlay;
private TextView tvStatus;
+ private TextView modalText;
+ private ImageView modalIcon;
private ProgressBar progressBar;
private ImageView ivCardReader;
+ // Animation
+ private Animation fadeIn;
+ private Animation fadeOut;
+
// Transaction Data
private String transactionAmount;
private boolean isEMVMode;
private boolean isProcessing = false;
+ private boolean isModalShowing = false;
// EMV Components
private EMVOptV2 mEMVOptV2;
@@ -85,6 +98,11 @@ public class EmvTransactionActivity extends AppCompatActivity {
private static final int PIN_CLICK_CONFIRM = 52;
private static final int PIN_CLICK_CANCEL = 53;
private static final int PIN_ERROR = 54;
+
+ // Modal Display Messages
+ private static final int SHOW_MODAL_SCAN_CARD = 101;
+ private static final int SHOW_MODAL_PROCESSING = 102;
+ private static final int HIDE_MODAL = 103;
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -110,10 +128,12 @@ public class EmvTransactionActivity extends AppCompatActivity {
case EMV_SHOW_PIN_PAD:
android.util.Log.d(TAG, "Initializing PIN pad...");
+ hideModal();
initPinPad();
break;
case EMV_ONLINE_PROCESS:
+ showModalProcessing("Memproses Otorisasi Online...");
mockOnlineProcess();
break;
@@ -149,6 +169,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
Log.e(TAG, "EMV Transaction failed: " + errorDesc + " (Code: " + errorCode + ")");
+ hideModal();
if (errorCode == -50009) {
// EMV process conflict - reset and retry
Log.d(TAG, "EMV process conflict detected - resetting...");
@@ -163,8 +184,22 @@ public class EmvTransactionActivity extends AppCompatActivity {
case EMV_TRANS_SUCCESS:
// Navigate to results screen
+ hideModal();
navigateToResults();
break;
+
+ case SHOW_MODAL_SCAN_CARD:
+ showModalScanCard();
+ break;
+
+ case SHOW_MODAL_PROCESSING:
+ String processingMsg = (String) msg.obj;
+ showModalProcessing(processingMsg);
+ break;
+
+ case HIDE_MODAL:
+ hideModal();
+ break;
}
}
};
@@ -176,6 +211,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
getIntentData();
initViews();
+ initAnimations();
initEMVComponents();
initEMVData();
@@ -208,12 +244,96 @@ public class EmvTransactionActivity extends AppCompatActivity {
}
// Initialize UI components
+ mainContent = findViewById(R.id.main_content);
+ modalOverlay = findViewById(R.id.modal_overlay);
tvStatus = findViewById(R.id.tv_status);
progressBar = findViewById(R.id.progress_bar);
ivCardReader = findViewById(R.id.iv_card_reader);
+ modalText = findViewById(R.id.modal_text);
+ modalIcon = findViewById(R.id.modal_icon);
// Set initial state
updateStatusUI("Initializing scanner...", true);
+
+ // Setup modal overlay click to close
+ modalOverlay.setOnClickListener(v -> {
+ // Optional: close modal when clicking outside
+ // hideModal();
+ });
+ }
+
+ // ====== ANIMATION INITIALIZATION ======
+ private void initAnimations() {
+ // Create fade animations programmatically since we want to avoid new resources
+ fadeIn = AnimationUtils.loadAnimation(this, android.R.anim.fade_in);
+ fadeOut = AnimationUtils.loadAnimation(this, android.R.anim.fade_out);
+
+ // Set animation duration
+ fadeIn.setDuration(300);
+ fadeOut.setDuration(300);
+ }
+
+ // ====== MODAL METHODS ======
+ private void showModalScanCard() {
+ if (isModalShowing) return;
+
+ runOnUiThread(() -> {
+ 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");
+ });
+ }
+
+ private void showModalProcessing(String message) {
+ if (!isModalShowing) {
+ runOnUiThread(() -> {
+ 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
+ runOnUiThread(() -> {
+ modalText.setText(message);
+ Log.d(TAG, "Modal text updated: " + message);
+ });
+ }
+ }
+
+ private void hideModal() {
+ if (!isModalShowing) return;
+
+ runOnUiThread(() -> {
+ 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");
+ });
}
// ====== NEW METHOD TO UPDATE STATUS UI ======
@@ -314,6 +434,9 @@ public class EmvTransactionActivity extends AppCompatActivity {
updateStatusUI("Ready for card...\n\nPlease insert, swipe, or tap your card\n\nScanning will start automatically", true);
+ // Show modal for card scanning
+ mHandler.obtainMessage(SHOW_MODAL_SCAN_CARD).sendToTarget();
+
// Start scanning automatically
startCardCheck();
}
@@ -396,6 +519,9 @@ public class EmvTransactionActivity extends AppCompatActivity {
updateStatusUI("Scan error: " + errorMessage + "\n\nRetrying in 2 seconds...", false);
showToast(errorMessage);
+ // Hide modal on error
+ hideModal();
+
// Auto-restart scanning after error
restartAutoScanning();
}
@@ -460,39 +586,65 @@ public class EmvTransactionActivity extends AppCompatActivity {
@Override
public void findMagCard(Bundle info) throws RemoteException {
android.util.Log.d(TAG, "Simple Mode: findMagCard callback triggered");
- runOnUiThread(() -> handleSimpleCardResult(info, "MAGNETIC"));
+ runOnUiThread(() -> {
+ showModalProcessing("Kartu Ditemukan - Memproses...");
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ handleSimpleCardResult(info, "MAGNETIC");
+ }, 1500);
+ });
}
@Override
public void findICCard(String atr) throws RemoteException {
Bundle info = new Bundle();
info.putString("atr", atr);
- runOnUiThread(() -> handleSimpleCardResult(info, "IC"));
+ runOnUiThread(() -> {
+ showModalProcessing("Kartu IC Ditemukan - Memproses...");
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ handleSimpleCardResult(info, "IC");
+ }, 1500);
+ });
}
@Override
public void findRFCard(String uuid) throws RemoteException {
Bundle info = new Bundle();
info.putString("uuid", uuid);
- runOnUiThread(() -> handleSimpleCardResult(info, "NFC"));
+ runOnUiThread(() -> {
+ showModalProcessing("Kartu NFC Ditemukan - Memproses...");
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ handleSimpleCardResult(info, "NFC");
+ }, 1500);
+ });
}
@Override
public void onError(int code, String message) throws RemoteException {
runOnUiThread(() -> {
showToast("Card error: " + message);
+ hideModal();
stopCardCheck();
});
}
@Override
public void findICCardEx(Bundle info) throws RemoteException {
- runOnUiThread(() -> handleSimpleCardResult(info, "IC"));
+ runOnUiThread(() -> {
+ showModalProcessing("Kartu IC Ditemukan - Memproses...");
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ handleSimpleCardResult(info, "IC");
+ }, 1500);
+ });
}
@Override
public void findRFCardEx(Bundle info) throws RemoteException {
- runOnUiThread(() -> handleSimpleCardResult(info, "NFC"));
+ runOnUiThread(() -> {
+ showModalProcessing("Kartu NFC Ditemukan - Memproses...");
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ handleSimpleCardResult(info, "NFC");
+ }, 1500);
+ });
}
@Override
@@ -500,6 +652,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
runOnUiThread(() -> {
String msg = info.getString("message", "Unknown error");
showToast("Card error: " + msg);
+ hideModal();
stopCardCheck();
});
}
@@ -509,26 +662,38 @@ public class EmvTransactionActivity extends AppCompatActivity {
private final CheckCardCallbackV2 mEMVCheckCardCallback = new CheckCardCallbackV2.Stub() {
@Override
public void findMagCard(Bundle info) throws RemoteException {
- runOnUiThread(() -> handleSimpleCardResult(info, "MAGNETIC"));
+ runOnUiThread(() -> {
+ showModalProcessing("Kartu Magnetik Ditemukan - Memproses...");
+ new Handler(Looper.getMainLooper()).postDelayed(() -> {
+ handleSimpleCardResult(info, "MAGNETIC");
+ }, 1500);
+ });
}
@Override
public void findICCard(String atr) throws RemoteException {
MyApplication.app.basicOptV2.buzzerOnDevice(1, 2750, 200, 0);
mCardType = AidlConstantsV2.CardType.IC.getValue();
- runOnUiThread(() -> startEMVTransactionProcess());
+ runOnUiThread(() -> {
+ showModalProcessing("Kartu IC Ditemukan - Memulai EMV...");
+ startEMVTransactionProcess();
+ });
}
@Override
public void findRFCard(String uuid) throws RemoteException {
mCardType = AidlConstantsV2.CardType.NFC.getValue();
- runOnUiThread(() -> startEMVTransactionProcess());
+ runOnUiThread(() -> {
+ showModalProcessing("Kartu NFC Ditemukan - Memulai EMV...");
+ startEMVTransactionProcess();
+ });
}
@Override
public void onError(int code, String message) throws RemoteException {
runOnUiThread(() -> {
showToast("EMV Error: " + message);
+ hideModal();
stopCardCheck();
});
}
@@ -536,13 +701,19 @@ public class EmvTransactionActivity extends AppCompatActivity {
@Override
public void findICCardEx(Bundle info) throws RemoteException {
mCardType = AidlConstantsV2.CardType.IC.getValue();
- runOnUiThread(() -> startEMVTransactionProcess());
+ runOnUiThread(() -> {
+ showModalProcessing("Kartu IC Ditemukan - Memulai EMV...");
+ startEMVTransactionProcess();
+ });
}
@Override
public void findRFCardEx(Bundle info) throws RemoteException {
mCardType = AidlConstantsV2.CardType.NFC.getValue();
- runOnUiThread(() -> startEMVTransactionProcess());
+ runOnUiThread(() -> {
+ showModalProcessing("Kartu NFC Ditemukan - Memulai EMV...");
+ startEMVTransactionProcess();
+ });
}
@Override
@@ -550,6 +721,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
runOnUiThread(() -> {
String msg = info.getString("message", "Unknown error");
showToast("EMV Error: " + msg);
+ hideModal();
stopCardCheck();
});
}
@@ -601,12 +773,14 @@ public class EmvTransactionActivity extends AppCompatActivity {
bundle.putInt("cardType", mCardType);
updateStatusUI("EMV processing started...\nReading card data...", true);
+ showModalProcessing("Memproses Data Kartu EMV...");
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);
+ hideModal();
resetScanningState();
showToast("Error starting EMV: " + e.getMessage());
}
@@ -615,6 +789,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
} catch (Exception e) {
android.util.Log.e(TAG, "Error starting EMV transaction: " + e.getMessage());
e.printStackTrace();
+ hideModal();
resetScanningState();
showToast("Error starting EMV transaction: " + e.getMessage());
}
@@ -628,6 +803,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
android.util.Log.d(TAG, "onWaitAppSelect isFirstSelect:" + isFirstSelect);
mProcessStep = EMV_APP_SELECT;
String[] candidateNames = getCandidateNames(appNameList);
+ runOnUiThread(() -> hideModal());
mHandler.obtainMessage(EMV_APP_SELECT, candidateNames).sendToTarget();
}
@@ -751,6 +927,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
String displayText = "Card Number Detected:\n" + maskCardNumber(mCardNo) +
"\n\nConfirming automatically...";
updateStatusUI(displayText, true);
+ showModalProcessing("Mengkonfirmasi Nomor Kartu...");
// Auto-confirm after short delay
new Handler(Looper.getMainLooper()).postDelayed(() -> {
@@ -767,6 +944,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
String displayText = "Certificate Verification:\n" + mCertInfo +
"\n\nProcessing automatically...";
updateStatusUI(displayText, true);
+ showModalProcessing("Memverifikasi Sertifikat...");
// Auto-confirm after short delay
new Handler(Looper.getMainLooper()).postDelayed(() -> {
@@ -793,7 +971,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
PinPadConfigV2 pinPadConfig = new PinPadConfigV2();
pinPadConfig.setPinPadType(0);
pinPadConfig.setPinType(mPinType);
- pinPadConfig.setOrderNumKey(true);
+ pinPadConfig.setOrderNumKey(true); // Set to true for normal order, false for random
String panForPin = mCardNo.substring(mCardNo.length() - 13, mCardNo.length() - 1);
byte[] panBytes = panForPin.getBytes("US-ASCII");
@@ -837,6 +1015,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
runOnUiThread(() -> {
updateStatusUI("PIN confirmed, processing...", true);
+ showModalProcessing("PIN Dikonfirmasi - Memproses...");
});
if (pinBlock != null) {
@@ -855,6 +1034,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
runOnUiThread(() -> {
updateStatusUI("PIN input cancelled", false);
+ hideModal();
});
mHandler.obtainMessage(PIN_CLICK_CANCEL).sendToTarget();
@@ -867,6 +1047,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
runOnUiThread(() -> {
updateStatusUI("PIN error: " + msg, false);
+ hideModal();
});
mHandler.obtainMessage(PIN_ERROR, code, code, msg).sendToTarget();
@@ -926,6 +1107,7 @@ public class EmvTransactionActivity extends AppCompatActivity {
mProcessStep = 0;
updateStatusUI("Ready for card...\n\nPlease insert, swipe, or tap your card", true);
+ hideModal();
}
// ====== HELPER METHODS ======
@@ -1066,6 +1248,8 @@ public class EmvTransactionActivity extends AppCompatActivity {
isProcessing = false;
mProcessStep = 0;
+ hideModal();
+
} catch (Exception e) {
Log.e(TAG, "Error cleaning up on back press: " + e.getMessage());
}
@@ -1092,6 +1276,8 @@ public class EmvTransactionActivity extends AppCompatActivity {
mEMVOptV2.initEmvProcess();
}
+ hideModal();
+
} catch (Exception e) {
Log.e(TAG, "Error cleaning up EMV: " + e.getMessage());
}
diff --git a/app/src/main/res/anim/slide_down.xml b/app/src/main/res/anim/slide_down.xml
new file mode 100644
index 0000000..102c5e7
--- /dev/null
+++ b/app/src/main/res/anim/slide_down.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/anim/slide_up.xml b/app/src/main/res/anim/slide_up.xml
new file mode 100644
index 0000000..bfb5615
--- /dev/null
+++ b/app/src/main/res/anim/slide_up.xml
@@ -0,0 +1,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/activity_emv_transaction.xml b/app/src/main/res/layout/activity_emv_transaction.xml
index bd4f73b..b23ea86 100644
--- a/app/src/main/res/layout/activity_emv_transaction.xml
+++ b/app/src/main/res/layout/activity_emv_transaction.xml
@@ -1,35 +1,123 @@
-
-
-
-
-
+
+ android:orientation="vertical">
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -41,67 +129,32 @@
android:padding="32dp"
android:gravity="center">
-
-
-
-
+
-
+
-
-
-
-
-
-
+ android:lineSpacingExtra="4dp" />
+
-
+
-
\ No newline at end of file
+
\ No newline at end of file