attempt to fix QR scanner
Auto Tag on Version Change / check-version (push) Successful in 7s

This commit is contained in:
2026-09-21 07:16:47 +05:00
parent c356762baa
commit eb019a20a0
@@ -32,7 +32,10 @@ import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import androidx.activity.result.ActivityResult
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import java.util.UUID
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@@ -162,9 +165,21 @@ class TransferFragment : Fragment() {
} }
).also { mfaisaHandler = it } ).also { mfaisaHandler = it }
private val qrLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> // Registered manually against the activity registry in onViewCreated rather than with a
if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult // registerForActivityResult() field initializer: HomeActivity caches one TransferFragment
val raw = result.data?.getStringExtra(QrScannerActivity.EXTRA_QR_CONTENT) ?: return@registerForActivityResult // instance (cachedTransferFragment) and re-shows it after a replace(). A field initializer
// runs only once, so once that instance was destroyed the launcher stayed unregistered and
// launching it threw. Keying off viewLifecycleOwner re-registers on every view creation.
private val qrLauncherKey = "transfer_qr_" + UUID.randomUUID()
private var qrLauncher: ActivityResultLauncher<Intent>? = null
private fun launchQrScanner() {
qrLauncher?.launch(Intent(requireContext(), QrScannerActivity::class.java))
}
private fun onQrScanned(result: ActivityResult) {
if (result.resultCode != Activity.RESULT_OK) return
val raw = result.data?.getStringExtra(QrScannerActivity.EXTRA_QR_CONTENT) ?: return
// BML card/gateway QR — hand off to dedicated payment screen // BML card/gateway QR — hand off to dedicated payment screen
val bmlUrl = PaymvQrParser.extractBmlGatewayUrl(raw) val bmlUrl = PaymvQrParser.extractBmlGatewayUrl(raw)
@@ -173,7 +188,7 @@ class TransferFragment : Fragment() {
it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT" it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT"
} }
(requireActivity() as HomeActivity).navigateTo(R.id.nav_transfer, TransferFragment.newInstanceFromBmlQr(bmlUrl ?: raw, fromCard?.accountNumber)) (requireActivity() as HomeActivity).navigateTo(R.id.nav_transfer, TransferFragment.newInstanceFromBmlQr(bmlUrl ?: raw, fromCard?.accountNumber))
return@registerForActivityResult return
} }
// M-Faisa merchant QR — content is just the numeric qrCodeId. Only attempt the lookup // M-Faisa merchant QR — content is just the numeric qrCodeId. Only attempt the lookup
@@ -184,13 +199,13 @@ class TransferFragment : Fragment() {
if (trimmedRaw.length in 8..16 && trimmedRaw.all { it.isDigit() } && if (trimmedRaw.length in 8..16 && trimmedRaw.all { it.isDigit() } &&
app.mfaisaSessions.isNotEmpty()) { app.mfaisaSessions.isNotEmpty()) {
lookupMfaisaQrMerchant(trimmedRaw) lookupMfaisaQrMerchant(trimmedRaw)
return@registerForActivityResult return
} }
val qr = PaymvQrParser.parse(raw) val qr = PaymvQrParser.parse(raw)
if (qr == null || qr.accountNumber == null) { if (qr == null || qr.accountNumber == null) {
Toast.makeText(requireContext(), R.string.transfer_qr_invalid, Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(), R.string.transfer_qr_invalid, Toast.LENGTH_SHORT).show()
return@registerForActivityResult return
} }
// Cards can't pay PayMV QR — fall back to default account or clear selection // Cards can't pay PayMV QR — fall back to default account or clear selection
@@ -287,6 +302,10 @@ class TransferFragment : Fragment() {
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
qrLauncher = requireActivity().activityResultRegistry.register(
qrLauncherKey, viewLifecycleOwner, ActivityResultContracts.StartActivityForResult()
) { onQrScanned(it) }
setupFromDropdown() setupFromDropdown()
setupAccountLookup() setupAccountLookup()
@@ -340,7 +359,7 @@ class TransferFragment : Fragment() {
} }
binding.btnScanQr.setOnClickListener { binding.btnScanQr.setOnClickListener {
qrLauncher.launch(Intent(requireContext(), QrScannerActivity::class.java)) launchQrScanner()
} }
viewModel.connectivityErrors.observe(viewLifecycleOwner) { updateTransferButton() } viewModel.connectivityErrors.observe(viewLifecycleOwner) { updateTransferButton() }
@@ -369,7 +388,7 @@ class TransferFragment : Fragment() {
arguments?.getString(ARG_BML_QR_URL)?.let { bmlHandler().lookupQrMerchant(it) } arguments?.getString(ARG_BML_QR_URL)?.let { bmlHandler().lookupQrMerchant(it) }
if (arguments?.getBoolean(ARG_AUTO_SCAN, false) == true) { if (arguments?.getBoolean(ARG_AUTO_SCAN, false) == true) {
qrLauncher.launch(Intent(requireContext(), QrScannerActivity::class.java)) launchQrScanner()
} }
// Restore form state when view is recreated on the cached no-args instance // Restore form state when view is recreated on the cached no-args instance
@@ -2002,6 +2021,8 @@ class TransferFragment : Fragment() {
bmlHandler = null bmlHandler = null
mfaisaHandler?.clearState() mfaisaHandler?.clearState()
mfaisaHandler = null mfaisaHandler = null
// Unregistered automatically with viewLifecycleOwner; drop the stale handle.
qrLauncher = null
_binding = null _binding = null
} }