menambahkan mdal pada screen emv

This commit is contained in:
riz081 2025-06-23 10:52:04 +07:00
parent f403358554
commit 0af0e836b1
4 changed files with 340 additions and 81 deletions

View File

@ -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());
}

View 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>

View 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>

View File

@ -1,35 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/colorBackground"
tools:context=".kredit.EmvTransactionActivity">
<!-- Toolbar -->
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/primary_blue"
android:theme="@style/CustomToolbarTheme"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<!-- Main Content Container with Center Alignment -->
<!-- Main Content -->
<LinearLayout
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="24dp">
android:orientation="vertical">
<!-- Status Modal Card - Centered and Compact -->
<!-- Toolbar -->
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/primary_blue"
android:theme="@style/CustomToolbarTheme"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<!-- Main Content Container with Center Alignment -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:padding="24dp">
<!-- Status Modal Card - Centered and Compact -->
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:cardCornerRadius="16dp"
app:cardElevation="8dp"
app:cardBackgroundColor="@android:color/white">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="32dp"
android:gravity="center">
<!-- Header Title -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Card Scanner"
style="@style/HeaderTextStyle"
android:layout_marginBottom="24dp" />
<!-- Card Reader Animation/Icon -->
<ImageView
android:id="@+id/iv_card_reader"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_gravity="center"
android:layout_marginBottom="24dp"
android:src="@drawable/ic_card_insert"
android:scaleType="centerInside"
app:tint="@color/primary_blue" />
<!-- Status Text -->
<TextView
android:id="@+id/tv_status"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Ready for card scanning..."
style="@style/StatusTextStyle"
android:layout_marginBottom="24dp" />
<!-- Progress Indicator -->
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:visibility="visible"
android:indeterminateTint="@color/primary_blue"
android:layout_marginBottom="16dp" />
<!-- Instructions Text -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Please insert, tap, or swipe your card\nScanning will start automatically"
style="@style/HintTextStyle"
android:gravity="center" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</LinearLayout>
<!-- Modal Overlay with Blur Effect -->
<FrameLayout
android:id="@+id/modal_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#80000000"
android:visibility="gone"
android:clickable="true"
android:focusable="true">
<!-- Modal Content -->
<androidx.cardview.widget.CardView
android:id="@+id/modal_card"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="32dp"
app:cardCornerRadius="16dp"
app:cardElevation="8dp"
app:cardBackgroundColor="@android:color/white">
@ -41,67 +129,32 @@
android:padding="32dp"
android:gravity="center">
<!-- Header Title -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Card Scanner"
android:textSize="24sp"
android:textStyle="bold"
android:textColor="#2E3A47"
android:gravity="center"
android:fontFamily="@font/inter"
android:layout_marginBottom="24dp" />
<!-- Card Reader Animation/Icon -->
<!-- Card Icon -->
<ImageView
android:id="@+id/iv_card_reader"
android:layout_width="120dp"
android:layout_height="120dp"
android:layout_gravity="center"
android:layout_marginBottom="24dp"
android:id="@+id/modal_icon"
android:layout_width="80dp"
android:layout_height="80dp"
android:src="@drawable/ic_card_insert"
android:scaleType="centerInside"
android:layout_marginBottom="24dp"
android:scaleType="fitCenter"
android:adjustViewBounds="true"
app:tint="@color/primary_blue" />
<!-- Status Text -->
<!-- Main Text -->
<TextView
android:id="@+id/tv_status"
android:layout_width="match_parent"
android:id="@+id/modal_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Ready for card scanning..."
android:textSize="16sp"
android:textColor="#5A6C7D"
android:text="Silakan Tempelkan / Gesekkan / Masukkan Kartu ke Perangkat"
style="@style/StatusTextStyle"
android:textAlignment="center"
android:gravity="center"
android:lineSpacingExtra="4dp"
android:fontFamily="@font/inter"
android:layout_marginBottom="24dp" />
<!-- Progress Indicator -->
<ProgressBar
android:id="@+id/progress_bar"
style="?android:attr/progressBarStyle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center"
android:visibility="visible"
android:indeterminateTint="@color/primary_blue"
android:layout_marginBottom="16dp" />
<!-- Instructions Text -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Please insert, tap, or swipe your card\nScanning will start automatically"
android:textSize="14sp"
android:textColor="#8A9BAE"
android:gravity="center"
android:lineSpacingExtra="2dp"
android:fontFamily="@font/inter" />
android:lineSpacingExtra="4dp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
</FrameLayout>
</LinearLayout>
</FrameLayout>