From a03b1b1682b3803814823fd28ea041be9a52d9e7 Mon Sep 17 00:00:00 2001 From: Shihaam Abdul Rahman Date: Sat, 30 May 2026 22:53:56 +0500 Subject: [PATCH] improve QR scan flow part:1 unified --- .../basedbank/ui/home/DashboardFragment.kt | 30 ++++++-- .../basedbank/ui/home/PayWithCardFragment.kt | 74 ++++++++----------- .../sar/basedbank/ui/home/TransferFragment.kt | 42 ++++++----- 3 files changed, 78 insertions(+), 68 deletions(-) diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt index 0f025bb..e2d6aa3 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt @@ -4,13 +4,13 @@ import android.app.Activity import android.content.Context import android.content.Intent import android.os.Bundle +import androidx.activity.result.contract.ActivityResultContracts import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.TextView import android.widget.Toast -import androidx.activity.result.contract.ActivityResultContracts import androidx.core.content.ContextCompat import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat @@ -25,8 +25,8 @@ import sh.sar.basedbank.api.models.BankAccount import sh.sar.basedbank.api.mib.MibCard import sh.sar.basedbank.api.mib.MibFinanceDeal import sh.sar.basedbank.util.bmlapi.BmlCardParser -import sh.sar.basedbank.util.PaymvQrParser import sh.sar.basedbank.util.CredentialStore +import sh.sar.basedbank.util.PaymvQrParser import kotlin.math.abs import sh.sar.basedbank.databinding.FragmentDashboardBinding import sh.sar.basedbank.databinding.ItemForeignLimitBinding @@ -36,21 +36,35 @@ class DashboardFragment : Fragment() { private var _binding: FragmentDashboardBinding? = null private val binding get() = _binding!! private val viewModel: HomeViewModel by activityViewModels() - - private var pendingQrAccountNumber: String? = null + private var pendingQrCardNumber: String? = null private val qrLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult val raw = result.data?.getStringExtra(QrScannerActivity.EXTRA_QR_CONTENT) ?: return@registerForActivityResult + val cardNumber = pendingQrCardNumber.also { pendingQrCardNumber = null } val bmlUrl = PaymvQrParser.extractBmlGatewayUrl(raw) if (raw.startsWith("https://ebanking.bankofmaldives.com.mv/qrpay/") || bmlUrl != null) { (requireActivity() as HomeActivity).navigateTo( - R.id.nav_transfer, TransferFragment.newInstanceFromBmlQr(bmlUrl ?: raw, pendingQrAccountNumber) + R.id.nav_transfer, TransferFragment.newInstanceFromBmlQr(bmlUrl ?: raw, cardNumber) ) } else { - Toast.makeText(requireContext(), R.string.transfer_qr_invalid, Toast.LENGTH_SHORT).show() + val qr = PaymvQrParser.parse(raw) + if (qr?.accountNumber != null) { + Toast.makeText(requireContext(), R.string.card_qr_paymv_unsupported, Toast.LENGTH_SHORT).show() + val defaultFrom = CredentialStore(requireContext()).getDefaultAccountNumber() + (requireActivity() as HomeActivity).navigateTo( + R.id.nav_transfer, TransferFragment.newInstanceFromQr( + accountNumber = qr.accountNumber, + displayName = qr.merchantName ?: qr.accountNumber, + amount = qr.amount, + remarks = qr.purpose, + fromAccountNumber = defaultFrom + ) + ) + } else { + Toast.makeText(requireContext(), R.string.transfer_qr_invalid, Toast.LENGTH_SHORT).show() + } } - pendingQrAccountNumber = null } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { @@ -402,7 +416,7 @@ class DashboardFragment : Fragment() { if (isMib) { Toast.makeText(requireContext(), R.string.mib_qr_nfc_not_supported, Toast.LENGTH_SHORT).show() } else { - pendingQrAccountNumber = (item as CardItem.Bml).account.accountNumber + pendingQrCardNumber = (item as CardItem.Bml).account.accountNumber qrLauncher.launch(Intent(requireContext(), QrScannerActivity::class.java)) } } diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/PayWithCardFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/PayWithCardFragment.kt index d526d8a..86b04ea 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/PayWithCardFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/PayWithCardFragment.kt @@ -3,6 +3,7 @@ package sh.sar.basedbank.ui.home import android.app.Activity import android.content.Context import android.content.Intent +import androidx.activity.result.contract.ActivityResultContracts import android.graphics.Color import android.graphics.drawable.GradientDrawable import android.os.Bundle @@ -13,7 +14,6 @@ import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import android.widget.Toast -import androidx.activity.result.contract.ActivityResultContracts import android.view.animation.AccelerateInterpolator import android.view.animation.DecelerateInterpolator import androidx.core.view.ViewCompat @@ -60,8 +60,37 @@ class CardsFragment : Fragment() { private var cards: List = emptyList() private var currentCardPosition: Int = 0 private var cardWidth: Int = 0 - private var pendingQrAccountNumber: String? = null + private var pendingQrCardNumber: String? = null private var isManageMode: Boolean = false + + private val qrLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult + val raw = result.data?.getStringExtra(QrScannerActivity.EXTRA_QR_CONTENT) ?: return@registerForActivityResult + val cardNumber = pendingQrCardNumber.also { pendingQrCardNumber = null } + val bmlUrl = PaymvQrParser.extractBmlGatewayUrl(raw) + if (raw.startsWith("https://ebanking.bankofmaldives.com.mv/qrpay/") || bmlUrl != null) { + (requireActivity() as HomeActivity).navigateTo( + R.id.nav_transfer, TransferFragment.newInstanceFromBmlQr(bmlUrl ?: raw, cardNumber) + ) + } else { + val qr = PaymvQrParser.parse(raw) + if (qr?.accountNumber != null) { + Toast.makeText(requireContext(), R.string.card_qr_paymv_unsupported, Toast.LENGTH_SHORT).show() + val defaultFrom = store.getDefaultAccountNumber() + (requireActivity() as HomeActivity).navigateTo( + R.id.nav_transfer, TransferFragment.newInstanceFromQr( + accountNumber = qr.accountNumber, + displayName = qr.merchantName ?: qr.accountNumber, + amount = qr.amount, + remarks = qr.purpose, + fromAccountNumber = defaultFrom + ) + ) + } else { + Toast.makeText(requireContext(), R.string.transfer_qr_invalid, Toast.LENGTH_SHORT).show() + } + } + } private var isTapMode: Boolean = false private var tapAnimView: NfcTapAnimationView? = null private var autoTapModeTriggered = false @@ -78,45 +107,6 @@ class CardsFragment : Fragment() { private lateinit var stackAdapter: CardStackAdapter private val store by lazy { CredentialStore(requireContext()) } - private val qrLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> - if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult - val raw = result.data?.getStringExtra(QrScannerActivity.EXTRA_QR_CONTENT) ?: return@registerForActivityResult - val bmlUrl = PaymvQrParser.extractBmlGatewayUrl(raw) - if (raw.startsWith("https://ebanking.bankofmaldives.com.mv/qrpay/") || bmlUrl != null) { - (requireActivity() as HomeActivity).navigateTo( - R.id.nav_transfer, TransferFragment.newInstanceFromBmlQr(bmlUrl ?: raw, pendingQrAccountNumber) - ) - } else { - val qr = PaymvQrParser.parse(raw) - if (qr?.accountNumber != null) { - // PayMV QR — cards are not supported for this payment type. - // Navigate to transfer with the recipient pre-filled; use default account if configured. - Toast.makeText(requireContext(), R.string.card_qr_paymv_unsupported, Toast.LENGTH_SHORT).show() - val defaultFromNumber = store.getDefaultAccountNumber() - val fragment = if (defaultFromNumber != null) { - TransferFragment.newInstanceFromQrWithFrom( - accountNumber = qr.accountNumber, - displayName = qr.merchantName ?: qr.accountNumber, - amount = qr.amount, - remarks = qr.purpose, - fromAccountNumber = defaultFromNumber - ) - } else { - TransferFragment.newInstanceFromQr( - accountNumber = qr.accountNumber, - displayName = qr.merchantName ?: qr.accountNumber, - amount = qr.amount, - remarks = qr.purpose - ) - } - (requireActivity() as HomeActivity).navigateTo(R.id.nav_transfer, fragment) - } else { - Toast.makeText(requireContext(), R.string.transfer_qr_invalid, Toast.LENGTH_SHORT).show() - } - } - pendingQrAccountNumber = null - } - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { _binding = FragmentCardsBinding.inflate(inflater, container, false) return binding.root @@ -228,7 +218,7 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets -> if (item is CardItem.Mib) { Toast.makeText(requireContext(), R.string.mib_qr_nfc_not_supported, Toast.LENGTH_SHORT).show() } else { - pendingQrAccountNumber = (item as CardItem.Bml).account.accountNumber + pendingQrCardNumber = (item as CardItem.Bml).account.accountNumber qrLauncher.launch(Intent(requireContext(), QrScannerActivity::class.java)) } } diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt index 709eeab..3ce4568 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt @@ -139,6 +139,28 @@ class TransferFragment : Fragment() { Toast.makeText(requireContext(), R.string.transfer_qr_invalid, Toast.LENGTH_SHORT).show() return@registerForActivityResult } + + // Cards can't pay PayMV QR — fall back to default account or clear selection + val isCard = selectedAccount?.let { + it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT" + } ?: false + if (isCard) { + Toast.makeText(requireContext(), R.string.card_qr_paymv_unsupported, Toast.LENGTH_SHORT).show() + val defaultNum = CredentialStore(requireContext()).getDefaultAccountNumber() + val defaultAcc = defaultNum?.let { num -> viewModel.accounts.value?.firstOrNull { it.accountNumber == num } } + selectedAccount = defaultAcc + binding.tilAmount.prefixText = null + if (defaultAcc != null) { + updateAmountPrefix(defaultAcc) + showFromCard(defaultAcc) + } else { + binding.cardFromInfo.visibility = View.GONE + binding.tilFrom.visibility = View.VISIBLE + binding.actvFrom.setText("", false) + } + updateTransferButton() + } + if (qr.amount != null) binding.etAmount.setText(qr.amount) if (qr.purpose != null) binding.etRemarks.setText(qr.purpose) prefillToFromContact(qr.accountNumber, "") @@ -188,34 +210,18 @@ class TransferFragment : Fragment() { } fun newInstanceFromQr( - accountNumber: String, - displayName: String, - amount: String?, - remarks: String? - ) = TransferFragment().apply { - arguments = Bundle().apply { - putString(ARG_ACCOUNT, accountNumber) - putString(ARG_NAME, displayName) - putString(ARG_SUBTITLE, accountNumber) - putString(ARG_COLOR, "#607D8B") - if (amount != null) putString(ARG_AMOUNT_PREFILL, amount) - if (remarks != null) putString(ARG_REMARKS_PREFILL, remarks) - } - } - - fun newInstanceFromQrWithFrom( accountNumber: String, displayName: String, amount: String?, remarks: String?, - fromAccountNumber: String + fromAccountNumber: String? = null ) = TransferFragment().apply { arguments = Bundle().apply { putString(ARG_ACCOUNT, accountNumber) putString(ARG_NAME, displayName) putString(ARG_SUBTITLE, accountNumber) putString(ARG_COLOR, "#607D8B") - putString(ARG_FROM_ACCOUNT, fromAccountNumber) + if (fromAccountNumber != null) putString(ARG_FROM_ACCOUNT, fromAccountNumber) if (amount != null) putString(ARG_AMOUNT_PREFILL, amount) if (remarks != null) putString(ARG_REMARKS_PREFILL, remarks) }