update UI QRIS Result

This commit is contained in:
riz081 2025-06-03 17:17:46 +07:00
parent 74f95e0374
commit a30e767adc
2 changed files with 464 additions and 119 deletions

View File

@ -3,16 +3,23 @@ package com.example.bdkipoc;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
@ -33,11 +40,23 @@ public class QrisResultActivity extends AppCompatActivity {
private ImageView qrImageView;
private TextView amountTextView;
private TextView referenceTextView;
private TextView timerTextView;
private TextView qrStatusTextView;
private Button downloadQrisButton;
private Button checkStatusButton;
private Button cancelButton;
private TextView statusTextView;
private Button returnMainButton;
private ProgressBar progressBar;
private LinearLayout backNavigation;
// Timer and QR refresh
private CountDownTimer countDownTimer;
private long timeLeftInMillis = 60000; // 60 seconds
private int qrRefreshCount = 0;
private final int MAX_QR_REFRESH = 5; // Maximum 5 refreshes
// Data variables
private String orderId;
private String grossAmount;
private String referenceId;
@ -45,26 +64,102 @@ public class QrisResultActivity extends AppCompatActivity {
private String transactionTime;
private String acquirer;
private String merchantId;
private String originalQrImageUrl;
private int amount;
private String backendBase = "https://be-edc.msvc.app";
private String webhookUrl = "https://be-edc.msvc.app/webhooks/midtrans";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set status bar color programmatically
setStatusBarColor();
setContentView(R.layout.activity_qris_result);
initializeViews();
setupClickListeners();
setupData();
startTimer();
}
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() {
qrImageView = findViewById(R.id.qrImageView);
amountTextView = findViewById(R.id.amountTextView);
referenceTextView = findViewById(R.id.referenceTextView);
timerTextView = findViewById(R.id.timerTextView);
qrStatusTextView = findViewById(R.id.qrStatusTextView);
downloadQrisButton = findViewById(R.id.downloadQrisButton);
checkStatusButton = findViewById(R.id.checkStatusButton);
cancelButton = findViewById(R.id.cancelButton);
statusTextView = findViewById(R.id.statusTextView);
returnMainButton = findViewById(R.id.returnMainButton);
progressBar = findViewById(R.id.progressBar);
backNavigation = findViewById(R.id.back_navigation);
}
private void setupClickListeners() {
// Back navigation
if (backNavigation != null) {
backNavigation.setOnClickListener(v -> finish());
}
// Cancel button
cancelButton.setOnClickListener(v -> {
if (countDownTimer != null) {
countDownTimer.cancel();
}
finish();
});
// Download QRIS button (now visible and functional)
downloadQrisButton.setOnClickListener(v -> {
qrImageView.setDrawingCacheEnabled(true);
Bitmap bitmap = qrImageView.getDrawingCache();
if (bitmap != null) {
saveImageToGallery(bitmap, "qris_code_" + System.currentTimeMillis());
}
qrImageView.setDrawingCacheEnabled(false);
});
// Check Payment Status button (now visible and functional)
checkStatusButton.setOnClickListener(v -> {
Toast.makeText(this, "Checking payment status...", Toast.LENGTH_SHORT).show();
simulateWebhook();
});
// Return to Main Screen button (now visible and functional)
returnMainButton.setOnClickListener(v -> {
if (countDownTimer != null) {
countDownTimer.cancel();
}
Intent intent = new Intent(QrisResultActivity.this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finishAffinity();
});
}
private void setupData() {
Intent intent = getIntent();
String qrImageUrl = intent.getStringExtra("qrImageUrl");
int amount = intent.getIntExtra("amount", 0);
originalQrImageUrl = qrImageUrl; // Store original URL for refresh
amount = intent.getIntExtra("amount", 0);
referenceId = intent.getStringExtra("referenceId");
orderId = intent.getStringExtra("orderId");
grossAmount = intent.getStringExtra("grossAmount");
@ -75,60 +170,131 @@ public class QrisResultActivity extends AppCompatActivity {
if (orderId == null || transactionId == null) {
Log.e("QrisResultFlow", "orderId or transactionId is null! Intent extras: " + intent.getExtras());
android.widget.Toast.makeText(this, "Missing transaction details!", android.widget.Toast.LENGTH_LONG).show();
Toast.makeText(this, "Missing transaction details!", Toast.LENGTH_LONG).show();
}
// Get the exact amount from the grossAmount string value instead of the integer
String amountStr = "Amount: " + grossAmount;
amountTextView.setText(amountStr);
referenceTextView.setText("Reference ID: " + referenceId);
// Format and display amount
if (grossAmount != null) {
String formattedAmount = "RP." + formatCurrency(grossAmount);
amountTextView.setText(formattedAmount);
} else if (amount > 0) {
String formattedAmount = "RP." + formatCurrency(String.valueOf(amount));
amountTextView.setText(formattedAmount);
}
// Load QR image
new DownloadImageTask(qrImageView).execute(qrImageUrl);
// Set reference ID (hidden)
if (referenceId != null) {
referenceTextView.setText("Reference ID: " + referenceId);
}
// Load initial QR image
if (qrImageUrl != null) {
loadQrCode(qrImageUrl);
}
// Disable check status button initially
checkStatusButton.setEnabled(false);
// Start polling for pending payment log
pollPendingPaymentLog(orderId);
if (orderId != null) {
pollPendingPaymentLog(orderId);
}
}
// Download QRIS button
downloadQrisButton.setOnClickListener(new View.OnClickListener() {
private String formatCurrency(String amount) {
try {
long number = Long.parseLong(amount.replace(".00", ""));
return String.format("%,d", number).replace(',', '.');
} catch (NumberFormatException e) {
return amount;
}
}
private void startTimer() {
countDownTimer = new CountDownTimer(timeLeftInMillis, 1000) {
@Override
public void onClick(View v) {
qrImageView.setDrawingCacheEnabled(true);
Bitmap bitmap = qrImageView.getDrawingCache();
if (bitmap != null) {
saveImageToGallery(bitmap, "qris_code_" + System.currentTimeMillis());
public void onTick(long millisUntilFinished) {
timeLeftInMillis = millisUntilFinished;
int seconds = (int) (millisUntilFinished / 1000);
timerTextView.setText(String.valueOf(seconds));
// Update status text based on remaining time
if (seconds > 10) {
qrStatusTextView.setText("QR Code akan refresh dalam " + seconds + " detik");
qrStatusTextView.setTextColor(Color.parseColor("#666666"));
} else {
qrStatusTextView.setText("QR Code akan refresh dalam " + seconds + " detik");
qrStatusTextView.setTextColor(Color.parseColor("#E31937"));
}
qrImageView.setDrawingCacheEnabled(false);
}
});
// Check Payment Status button
checkStatusButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
simulateWebhook();
public void onFinish() {
timerTextView.setText("0");
qrStatusTextView.setText("Refreshing QR Code...");
qrStatusTextView.setTextColor(Color.parseColor("#E31937"));
// Auto refresh QR code
refreshQrCode();
}
});
}.start();
}
// Return to Main Screen button
returnMainButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(QrisResultActivity.this, com.example.bdkipoc.MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
finishAffinity();
private void refreshQrCode() {
qrRefreshCount++;
if (qrRefreshCount >= MAX_QR_REFRESH) {
// Max refresh reached, show expired message
timerTextView.setText("Expired");
qrStatusTextView.setText("QR Code telah expired. Silahkan generate ulang.");
qrStatusTextView.setTextColor(Color.parseColor("#E31937"));
Toast.makeText(this, "QR Code expired. Please generate new payment.", Toast.LENGTH_LONG).show();
return;
}
// Show loading state
progressBar.setVisibility(View.VISIBLE);
qrStatusTextView.setText("Generating new QR Code... (" + qrRefreshCount + "/" + MAX_QR_REFRESH + ")");
// Simulate generating new QR code
new Thread(() -> {
try {
// Simulate API call delay
Thread.sleep(2000);
// Generate new QR code (in real app, call Midtrans API again)
generateNewQrCode();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
private void generateNewQrCode() {
new Handler(Looper.getMainLooper()).post(() -> {
progressBar.setVisibility(View.GONE);
// In real implementation, you would call Midtrans API again
// For demo, we'll reload the same QR with a timestamp to show refresh
String refreshedUrl = originalQrImageUrl + "?refresh=" + System.currentTimeMillis();
loadQrCode(refreshedUrl);
// Reset timer for new 60 seconds
timeLeftInMillis = 60000;
startTimer();
Toast.makeText(this, "QR Code refreshed! (" + qrRefreshCount + "/" + MAX_QR_REFRESH + ")", Toast.LENGTH_SHORT).show();
});
}
private void loadQrCode(String qrImageUrl) {
new DownloadImageTask(qrImageView).execute(qrImageUrl);
}
private static class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urlDisplay = urls[0];
Bitmap bitmap = null;
@ -144,6 +310,7 @@ public class QrisResultActivity extends AppCompatActivity {
}
return bitmap;
}
protected void onPostExecute(Bitmap result) {
if (result != null) {
bmImage.setImageBitmap(result);
@ -157,12 +324,12 @@ public class QrisResultActivity extends AppCompatActivity {
String savedImageURL = android.provider.MediaStore.Images.Media.insertImage(
getContentResolver(), bitmap, fileName, "QRIS Payment QR Code");
if (savedImageURL != null) {
android.widget.Toast.makeText(this, "QRIS saved to gallery", android.widget.Toast.LENGTH_SHORT).show();
Toast.makeText(this, "QRIS saved to gallery", Toast.LENGTH_SHORT).show();
} else {
android.widget.Toast.makeText(this, "Failed to save QRIS", android.widget.Toast.LENGTH_SHORT).show();
Toast.makeText(this, "Failed to save QRIS", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
android.widget.Toast.makeText(this, "Error saving QRIS: " + e.getMessage(), android.widget.Toast.LENGTH_LONG).show();
Toast.makeText(this, "Error saving QRIS: " + e.getMessage(), Toast.LENGTH_LONG).show();
}
}
@ -216,9 +383,9 @@ public class QrisResultActivity extends AppCompatActivity {
progressBar.setVisibility(View.GONE);
if (logFound) {
checkStatusButton.setEnabled(true);
android.widget.Toast.makeText(QrisResultActivity.this, "Pending payment log found!", android.widget.Toast.LENGTH_SHORT).show();
Toast.makeText(QrisResultActivity.this, "Pending payment log found!", Toast.LENGTH_SHORT).show();
} else {
android.widget.Toast.makeText(QrisResultActivity.this, "Pending payment log NOT found.", android.widget.Toast.LENGTH_LONG).show();
Toast.makeText(QrisResultActivity.this, "Pending payment log NOT found.", Toast.LENGTH_LONG).show();
}
});
}).start();
@ -273,15 +440,52 @@ public class QrisResultActivity extends AppCompatActivity {
}
new Handler(Looper.getMainLooper()).post(() -> {
progressBar.setVisibility(View.GONE);
// Proceed to show status/result
qrImageView.setVisibility(View.GONE);
amountTextView.setVisibility(View.GONE);
referenceTextView.setVisibility(View.GONE);
downloadQrisButton.setVisibility(View.GONE);
checkStatusButton.setVisibility(View.GONE);
statusTextView.setVisibility(View.VISIBLE);
returnMainButton.setVisibility(View.VISIBLE);
// Show success state
showSuccessState();
});
}).start();
}
}
private void showSuccessState() {
// Stop timer
if (countDownTimer != null) {
countDownTimer.cancel();
}
// Hide QR section and show success
qrImageView.setVisibility(View.GONE);
amountTextView.setVisibility(View.GONE);
timerTextView.setVisibility(View.GONE);
qrStatusTextView.setVisibility(View.GONE);
downloadQrisButton.setVisibility(View.GONE);
checkStatusButton.setVisibility(View.GONE);
cancelButton.setVisibility(View.GONE);
// Show success message
statusTextView.setText("Payment Successful!");
statusTextView.setTextColor(Color.parseColor("#4CAF50"));
statusTextView.setTextSize(24);
statusTextView.setVisibility(View.VISIBLE);
returnMainButton.setVisibility(View.VISIBLE);
returnMainButton.setText("Back to Main");
returnMainButton.setBackgroundColor(Color.parseColor("#4CAF50"));
Toast.makeText(this, "Payment completed successfully!", Toast.LENGTH_LONG).show();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (countDownTimer != null) {
countDownTimer.cancel();
}
}
@Override
public void onBackPressed() {
if (countDownTimer != null) {
countDownTimer.cancel();
}
super.onBackPressed();
}
}

View File

@ -1,82 +1,223 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="24dp"
android:background="#181824"
>
android:background="#FFFFFF">
<TextView
android:id="@+id/amountTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/amount"
android:textColor="#2D5DA1"
android:textSize="20sp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"/>
<!-- Red Status Bar -->
<View
android:layout_width="match_parent"
android:layout_height="44dp"
android:background="#E31937" />
<TextView
android:id="@+id/referenceTextView"
android:layout_width="wrap_content"
<!-- Header with Back Navigation -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/reference_id"
android:textColor="#2D5DA1"
android:background="#E31937"
android:paddingBottom="16dp">
<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:background="?attr/selectableItemBackgroundBorderless"
android:padding="8dp"
android:clickable="true"
android:focusable="true">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textColor="@android:color/white"
android:textSize="18sp"
android:textStyle="bold"
android:layout_marginEnd="8dp" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Kembali"
android:textColor="@android:color/white"
android:textSize="14sp" />
</LinearLayout>
</LinearLayout>
<!-- White Card Container -->
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:layout_marginTop="0dp"
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="24dp"
android:gravity="center_horizontal">
<!-- Generate QR Title -->
<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
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="QRIS"
android:textColor="@android:color/black"
android:textSize="32sp"
android:textStyle="bold"
android:fontFamily="monospace"
android:layout_marginBottom="24dp" />
<!-- QR Code -->
<ImageView
android:id="@+id/qrImageView"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_gravity="center_horizontal"
android:contentDescription="QRIS QR Code"
android:scaleType="fitCenter"
android:background="#F0F0F0"
android:layout_marginBottom="24dp" />
<!-- Amount Display -->
<TextView
android:id="@+id/amountTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RP.200.000"
android:textColor="@android:color/black"
android:textSize="20sp"
android:textStyle="bold"
android:layout_marginBottom="16dp" />
<!-- Timer/Counter -->
<TextView
android:id="@+id/timerTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="60"
android:textColor="#E31937"
android:textSize="18sp"
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:textSize="12sp"
android:layout_marginBottom="16dp"
android:visibility="visible" />
<!-- 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>
<!-- Hidden views for compatibility -->
<TextView
android:id="@+id/referenceTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Reference ID: ref-12345"
android:textColor="@android:color/black"
android:textSize="14sp"
android:visibility="gone" />
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone" />
<TextView
android:id="@+id/statusTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Payment Status Success"
android:textColor="@android:color/black"
android:textSize="18sp"
android:visibility="gone" />
</LinearLayout>
</androidx.cardview.widget.CardView>
<!-- Spacer to push button to bottom -->
<View
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<!-- Bottom Cancel Button -->
<Button
android:id="@+id/cancelButton"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_margin="16dp"
android:text="Batalkan"
android:textColor="#E31937"
android:textSize="16sp"
android:layout_gravity="center_horizontal"
android:layout_marginBottom="16dp"
android:layout_marginTop="8dp"/>
android:textStyle="normal"
android:background="@android:color/transparent"
style="?android:attr/borderlessButtonStyle" />
<ImageView
android:id="@+id/qrImageView"
android:layout_width="280dp"
android:layout_height="280dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"
android:contentDescription="@string/qr_code"/>
<Button
android:id="@+id/downloadQrisButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/download_qris"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"/>
<Button
android:id="@+id/checkStatusButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/check_payment_status"
android:layout_gravity="center_horizontal"
android:layout_marginTop="16dp"/>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:visibility="gone"/>
<TextView
android:id="@+id/statusTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/payment_status_success"
android:textColor="#2D5DA1"
android:textSize="20sp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="24dp"
android:visibility="gone"/>
<Button
android:id="@+id/returnMainButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/return_main"
android:layout_gravity="center_horizontal"
android:layout_marginTop="24dp"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout>