forked from shihaam/thijooree
unified pay with QR and tranfer confirm dialog box
This commit is contained in:
@@ -197,7 +197,7 @@ class BmlQrPayFragment : Fragment() {
|
|||||||
val currency = info.currency
|
val currency = info.currency
|
||||||
|
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
.setTitle(R.string.bml_qr_pay_now)
|
.setTitle(R.string.transfer)
|
||||||
.setMessage("Pay $currency ${"%.2f".format(amount)} to ${info.merchantName}?\n\nFrom: ${account.accountBriefName} · ${account.accountNumber}")
|
.setMessage("Pay $currency ${"%.2f".format(amount)} to ${info.merchantName}?\n\nFrom: ${account.accountBriefName} · ${account.accountNumber}")
|
||||||
.setPositiveButton(R.string.transfer_confirm) { _, _ ->
|
.setPositiveButton(R.string.transfer_confirm) { _, _ ->
|
||||||
executePay(account, debitAccount, info.requestId, amount, currency, info.merchantName)
|
executePay(account, debitAccount, info.requestId, amount, currency, info.merchantName)
|
||||||
|
|||||||
@@ -709,8 +709,54 @@ class TransferFragment : Fragment() {
|
|||||||
|
|
||||||
// ── Transfer ──────────────────────────────────────────────────────────────
|
// ── Transfer ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Shared confirm dialog with optional biometric gate, used for both transfers and QR payments. */
|
||||||
|
private fun showConfirmWithBiometric(
|
||||||
|
title: String,
|
||||||
|
message: String? = null,
|
||||||
|
customView: android.view.View? = null,
|
||||||
|
biometricSubtitle: String,
|
||||||
|
onConfirmed: () -> Unit
|
||||||
|
) {
|
||||||
|
val builder = MaterialAlertDialogBuilder(requireContext())
|
||||||
|
.setTitle(title)
|
||||||
|
.setPositiveButton(R.string.transfer_confirm) { _, _ -> onConfirmed() }
|
||||||
|
.setNegativeButton(android.R.string.cancel, null)
|
||||||
|
if (customView != null) builder.setView(customView) else builder.setMessage(message)
|
||||||
|
val dialog = builder.show()
|
||||||
|
val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
|
||||||
|
val biometricTransferConfirm = prefs.getBoolean("biometrics_transfer_confirm", false)
|
||||||
|
val canAuth = BiometricManager.from(requireContext())
|
||||||
|
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS
|
||||||
|
if (biometricTransferConfirm && canAuth) {
|
||||||
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||||
|
val prompt = BiometricPrompt(this, ContextCompat.getMainExecutor(requireContext()),
|
||||||
|
object : BiometricPrompt.AuthenticationCallback() {
|
||||||
|
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||||
|
dialog.dismiss()
|
||||||
|
onConfirmed()
|
||||||
|
}
|
||||||
|
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
||||||
|
if (errorCode != BiometricPrompt.ERROR_CANCELED &&
|
||||||
|
errorCode != BiometricPrompt.ERROR_USER_CANCELED &&
|
||||||
|
errorCode != BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
|
||||||
|
Toast.makeText(requireContext(), errString, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
override fun onAuthenticationFailed() { /* keep dialog open */ }
|
||||||
|
})
|
||||||
|
prompt.authenticate(
|
||||||
|
BiometricPrompt.PromptInfo.Builder()
|
||||||
|
.setTitle(getString(R.string.biometric_transfer_title))
|
||||||
|
.setSubtitle(biometricSubtitle)
|
||||||
|
.setNegativeButtonText(getString(android.R.string.cancel))
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun initiateTransfer() {
|
private fun initiateTransfer() {
|
||||||
// BML QR merchant payment — completely separate flow
|
// BML QR merchant payment — uses shared confirm dialog, no receipt
|
||||||
bmlQrInfo?.let { info ->
|
bmlQrInfo?.let { info ->
|
||||||
val src = selectedAccount ?: run {
|
val src = selectedAccount ?: run {
|
||||||
Toast.makeText(requireContext(), R.string.transfer_session_unavailable, Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), R.string.transfer_session_unavailable, Toast.LENGTH_SHORT).show()
|
||||||
@@ -724,14 +770,12 @@ class TransferFragment : Fragment() {
|
|||||||
Toast.makeText(requireContext(), R.string.transfer_missing_internal_id, Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), R.string.transfer_missing_internal_id, Toast.LENGTH_SHORT).show()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
showConfirmWithBiometric(
|
||||||
.setTitle(R.string.bml_qr_pay_now)
|
title = getString(R.string.transfer),
|
||||||
.setMessage("Pay ${info.currency} ${"%.2f".format(amount)} to ${info.merchantName}?\n\nFrom: ${src.accountBriefName} · ${src.accountNumber}")
|
message = "Pay ${info.currency} ${"%.2f".format(amount)} to ${info.merchantName}?\n\nFrom: ${src.accountBriefName} · ${src.accountNumber}",
|
||||||
.setPositiveButton(R.string.transfer_confirm) { _, _ ->
|
biometricSubtitle = "${info.currency} ${"%.2f".format(amount)} → ${info.merchantName}",
|
||||||
executeBmlQrPayment(src, debitAccount, info, amount)
|
onConfirmed = { executeBmlQrPayment(src, debitAccount, info, amount) }
|
||||||
}
|
)
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
.show()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -820,30 +864,21 @@ class TransferFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val dialogBuilder = MaterialAlertDialogBuilder(requireContext())
|
val warningView: android.view.View? = if (isUsdToMvr || isSrcCredit) {
|
||||||
.setTitle(R.string.transfer)
|
|
||||||
.setPositiveButton(R.string.transfer_confirm) { _, _ -> doTransfer() }
|
|
||||||
.setNegativeButton(android.R.string.cancel, null)
|
|
||||||
|
|
||||||
if (isUsdToMvr || isSrcCredit) {
|
|
||||||
val ctx = requireContext()
|
val ctx = requireContext()
|
||||||
val dp = resources.displayMetrics.density
|
val dp = resources.displayMetrics.density
|
||||||
val container = LinearLayout(ctx).apply {
|
LinearLayout(ctx).apply {
|
||||||
orientation = LinearLayout.VERTICAL
|
orientation = LinearLayout.VERTICAL
|
||||||
setPadding((24 * dp).toInt(), (16 * dp).toInt(), (24 * dp).toInt(), 0)
|
setPadding((24 * dp).toInt(), (16 * dp).toInt(), (24 * dp).toInt(), 0)
|
||||||
}
|
addView(TextView(ctx).apply { text = mainMsg })
|
||||||
container.addView(TextView(ctx).apply { text = mainMsg })
|
if (isUsdToMvr) addView(TextView(ctx).apply {
|
||||||
if (isUsdToMvr) {
|
|
||||||
container.addView(TextView(ctx).apply {
|
|
||||||
text = "⚠ You are transferring from a USD account to an MVR account. The currency will be converted at the bank's rate and this cannot be reversed!"
|
text = "⚠ You are transferring from a USD account to an MVR account. The currency will be converted at the bank's rate and this cannot be reversed!"
|
||||||
setTextColor(Color.RED)
|
setTextColor(Color.RED)
|
||||||
textSize = 16f
|
textSize = 16f
|
||||||
typeface = Typeface.DEFAULT_BOLD
|
typeface = Typeface.DEFAULT_BOLD
|
||||||
setPadding(0, (16 * dp).toInt(), 0, 0)
|
setPadding(0, (16 * dp).toInt(), 0, 0)
|
||||||
})
|
})
|
||||||
}
|
if (isSrcCredit) addView(TextView(ctx).apply {
|
||||||
if (isSrcCredit) {
|
|
||||||
container.addView(TextView(ctx).apply {
|
|
||||||
text = "⚠ Transferring from a credit card is treated as a cash advance. Cash advance fees will be charged on the 10th of the month."
|
text = "⚠ Transferring from a credit card is treated as a cash advance. Cash advance fees will be charged on the 10th of the month."
|
||||||
setTextColor(Color.RED)
|
setTextColor(Color.RED)
|
||||||
textSize = 16f
|
textSize = 16f
|
||||||
@@ -851,43 +886,14 @@ class TransferFragment : Fragment() {
|
|||||||
setPadding(0, (16 * dp).toInt(), 0, 0)
|
setPadding(0, (16 * dp).toInt(), 0, 0)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
dialogBuilder.setView(container)
|
} else null
|
||||||
} else {
|
showConfirmWithBiometric(
|
||||||
dialogBuilder.setMessage(mainMsg)
|
title = getString(R.string.transfer),
|
||||||
}
|
message = if (warningView == null) mainMsg else null,
|
||||||
|
customView = warningView,
|
||||||
val dialog = dialogBuilder.show()
|
biometricSubtitle = "$currency $amountStr → $destDisplay",
|
||||||
|
onConfirmed = { doTransfer() }
|
||||||
val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
|
)
|
||||||
val biometricTransferConfirm = prefs.getBoolean("biometrics_transfer_confirm", false)
|
|
||||||
val canAuth = BiometricManager.from(requireContext())
|
|
||||||
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS
|
|
||||||
if (biometricTransferConfirm && canAuth) {
|
|
||||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
|
||||||
val prompt = BiometricPrompt(this, ContextCompat.getMainExecutor(requireContext()),
|
|
||||||
object : BiometricPrompt.AuthenticationCallback() {
|
|
||||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
|
||||||
dialog.dismiss()
|
|
||||||
doTransfer()
|
|
||||||
}
|
|
||||||
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
|
|
||||||
if (errorCode != BiometricPrompt.ERROR_CANCELED &&
|
|
||||||
errorCode != BiometricPrompt.ERROR_USER_CANCELED &&
|
|
||||||
errorCode != BiometricPrompt.ERROR_NEGATIVE_BUTTON) {
|
|
||||||
Toast.makeText(requireContext(), errString, Toast.LENGTH_SHORT).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
override fun onAuthenticationFailed() { /* keep dialog open */ }
|
|
||||||
})
|
|
||||||
prompt.authenticate(
|
|
||||||
BiometricPrompt.PromptInfo.Builder()
|
|
||||||
.setTitle(getString(R.string.biometric_transfer_title))
|
|
||||||
.setSubtitle("$currency $amountStr → $destDisplay")
|
|
||||||
.setNegativeButtonText(getString(android.R.string.cancel))
|
|
||||||
.build()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun executeBmlQrPayment(
|
private fun executeBmlQrPayment(
|
||||||
|
|||||||
@@ -203,7 +203,7 @@
|
|||||||
android:id="@+id/btnPay"
|
android:id="@+id/btnPay"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:text="@string/bml_qr_pay_now"
|
android:text="@string/transfer"
|
||||||
android:enabled="false" />
|
android:enabled="false" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|||||||
@@ -239,8 +239,7 @@
|
|||||||
<string name="transfer_otp_code_hint">Verification code</string>
|
<string name="transfer_otp_code_hint">Verification code</string>
|
||||||
|
|
||||||
<!-- BML QR Pay -->
|
<!-- BML QR Pay -->
|
||||||
<string name="bml_qr_pay_now">Pay Now</string>
|
<string name="bml_qr_looking_up">Looking up merchant…</string>
|
||||||
<string name="bml_qr_looking_up">Looking up merchant…</string>
|
|
||||||
<string name="bml_qr_lookup_failed">Could not load merchant details</string>
|
<string name="bml_qr_lookup_failed">Could not load merchant details</string>
|
||||||
<string name="bml_qr_payment_success">Payment Successful</string>
|
<string name="bml_qr_payment_success">Payment Successful</string>
|
||||||
<string name="bml_qr_select_account">Select a BML account to pay from</string>
|
<string name="bml_qr_select_account">Select a BML account to pay from</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user