7 Commits
Author SHA1 Message Date
shihaam 173c02ab8f release version 1.0.13
Build and Release APK / build (push) Successful in 3m12s
Auto Tag on Version Change / check-version (push) Failing after 14m33s
2026-05-31 00:28:28 +05:00
shihaam b37b12996f Contacts after selecting account back button Behaviour Enchantment #30
Auto Tag on Version Change / check-version (push) Successful in 5s
2026-05-31 00:26:10 +05:00
shihaam 21203b39e7 rearrange buttons in contact page #28
Auto Tag on Version Change / check-version (push) Failing after 15m6s
2026-05-31 00:22:55 +05:00
shihaam 0be492ca18 remove custom logic for scan button in PayMV QR generate and just handoff to transfer page to handle auto select account
Auto Tag on Version Change / check-version (push) Failing after 15m1s
2026-05-31 00:18:01 +05:00
shihaam 973576cf6a save static BML QR scans to recents #31
Auto Tag on Version Change / check-version (push) Successful in 6s
2026-05-31 00:06:37 +05:00
shihaam 4523aed69e remove *** on customer amount set PayMV QRs, #29
Auto Tag on Version Change / check-version (push) Successful in 7s
2026-05-30 23:26:55 +05:00
shihaam f90d83b59e improve share to scan to pay flow: fetching merchant details
Auto Tag on Version Change / check-version (push) Failing after 10m51s
2026-05-30 23:22:10 +05:00
9 changed files with 64 additions and 48 deletions
+2 -2
View File
@@ -11,8 +11,8 @@ android {
applicationId = "sh.sar.basedbank"
minSdk = 26
targetSdk = 36
versionCode = 11
versionName = "1.0.12"
versionCode = 12
versionName = "1.0.13"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -33,6 +33,8 @@ import sh.sar.basedbank.api.models.BankAccount
import sh.sar.basedbank.databinding.FragmentBmlQrPayBinding
import sh.sar.basedbank.databinding.ItemAccountDropdownBinding
import sh.sar.basedbank.util.CredentialStore
import sh.sar.basedbank.util.RecentPick
import sh.sar.basedbank.util.RecentsCache
import sh.sar.basedbank.util.Totp
class BmlQrPayFragment : Fragment() {
@@ -150,6 +152,19 @@ class BmlQrPayFragment : Fragment() {
return@launch
}
merchantInfo = info
if (info.amount == 0.0) {
val qrUrl = arguments?.getString(ARG_QR_URL)
if (qrUrl != null) {
RecentsCache.save(requireContext(), RecentPick(
accountNumber = "bmlqr:$qrUrl",
displayName = info.merchantName,
subtitle = info.merchantAddress.ifBlank { "BML Merchant" },
colorHex = "#0066A1",
imageHash = null,
isProfileImage = false
))
}
}
populateMerchant(info)
}
}
@@ -168,6 +168,10 @@ class ContactPickerSheetFragment : BottomSheetDialogFragment() {
val account = accounts.firstOrNull { it.accountNumber == accountNumber }
val bundle = bundleOf(KEY_ACCOUNT_NUMBER to accountNumber, KEY_LABEL to label)
when {
accountNumber.startsWith("bmlqr:") -> {
bundle.putString(KEY_SUBTITLE, "BML QR Merchant")
bundle.putString(KEY_COLOR, "#0066A1")
}
account != null -> {
bundle.putString(KEY_SUBTITLE, "${account.accountNumber} · ${account.currencyName} ${account.availableBalance}")
bundle.putString(KEY_COLOR, "#FE860E")
@@ -178,7 +178,7 @@ class ContactsFragment : Fragment() {
colorHex = contact.bankColor,
imageHash = contact.imageHash
)
(requireActivity() as HomeActivity).navigateTo(R.id.nav_transfer, fragment)
(requireActivity() as HomeActivity).showWithBackStack(fragment)
}
private fun confirmDelete(contact: ContactDisplay) {
@@ -446,18 +446,20 @@ fun applyNavLabelVisibility() {
}
private fun routeSharedQrText(text: String) {
val store = CredentialStore(this)
val bmlUrl = sh.sar.basedbank.util.PaymvQrParser.extractBmlGatewayUrl(text)
if (text.startsWith("https://ebanking.bankofmaldives.com.mv/qrpay/") || bmlUrl != null) {
navigateTo(R.id.nav_transfer, TransferFragment.newInstanceFromBmlQr(bmlUrl ?: text))
navigateTo(R.id.nav_transfer, TransferFragment.newInstanceFromBmlQr(bmlUrl ?: text, store.getDefaultCardAccountNumber()))
return
}
val qr = sh.sar.basedbank.util.PaymvQrParser.parse(text)
if (qr?.accountNumber != null) {
navigateTo(R.id.nav_transfer, TransferFragment.newInstanceFromQr(
accountNumber = qr.accountNumber,
displayName = qr.merchantName ?: qr.accountNumber,
amount = qr.amount,
remarks = qr.purpose
accountNumber = qr.accountNumber,
displayName = qr.merchantName ?: qr.accountNumber,
amount = qr.amount,
remarks = qr.purpose,
fromAccountNumber = store.getDefaultAccountNumber()
))
} else {
Toast.makeText(this, R.string.transfer_qr_invalid, Toast.LENGTH_SHORT).show()
@@ -7,13 +7,10 @@ import android.os.Build
import android.os.Bundle
import android.os.Environment
import android.provider.MediaStore
import android.app.Activity
import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.content.FileProvider
import androidx.core.view.ViewCompat
@@ -39,7 +36,6 @@ import sh.sar.basedbank.databinding.FragmentPayMvQrBinding
import sh.sar.basedbank.util.CredentialStore
import sh.sar.basedbank.databinding.ItemAccountDropdownBinding
import sh.sar.basedbank.util.AccountListParser
import sh.sar.basedbank.util.PaymvQrParser
import sh.sar.basedbank.util.bmlapi.BmlCardParser
import sh.sar.basedbank.util.bmlapi.BmlDashboardParser
import java.io.File
@@ -56,31 +52,6 @@ class PayMvQrFragment : Fragment() {
private var generateJob: Job? = null
private val dropdownProfileImageCache = mutableMapOf<String, Bitmap>()
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
// BML card/gateway QR — hand off to dedicated payment screen
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))
return@registerForActivityResult
}
val qr = PaymvQrParser.parse(raw)
if (qr == null || qr.accountNumber == null) {
Toast.makeText(requireContext(), R.string.transfer_qr_invalid, Toast.LENGTH_SHORT).show()
return@registerForActivityResult
}
val activity = requireActivity() as HomeActivity
activity.navigateTo(R.id.nav_transfer, TransferFragment.newInstanceFromQr(
accountNumber = qr.accountNumber,
displayName = qr.merchantName ?: qr.accountNumber,
amount = qr.amount,
remarks = qr.purpose
))
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
): View {
@@ -106,7 +77,7 @@ class PayMvQrFragment : Fragment() {
binding.btnShare.setOnClickListener { shareQr() }
binding.btnSave.setOnClickListener { saveQr() }
binding.btnScanQr.setOnClickListener {
qrLauncher.launch(Intent(requireContext(), QrScannerActivity::class.java))
(requireActivity() as HomeActivity).navigateTo(R.id.nav_transfer, TransferFragment.newInstanceWithAutoScan())
}
}
@@ -95,6 +95,7 @@ class TransferFragment : Fragment() {
// BML QR merchant payment mode (set when navigated from a card QR scan)
private var bmlQrInfo: BmlQrPayInfo? = null
private var bmlGatewayQr = false // true for pay.bml.com.mv QRs (requires pre-initiate step)
private var bmlQrLookupAttempted = false // prevents re-lookup after user clears the merchant
private val dropdownProfileImageCache = mutableMapOf<String, Bitmap>()
// BML business profile OTP flow state
@@ -245,6 +246,10 @@ class TransferFragment : Fragment() {
childFragmentManager.setFragmentResultListener(ContactPickerSheetFragment.REQUEST_KEY, viewLifecycleOwner) { _, bundle ->
val accountNumber = bundle.getString(ContactPickerSheetFragment.KEY_ACCOUNT_NUMBER) ?: return@setFragmentResultListener
if (accountNumber.startsWith("bmlqr:")) {
lookupBmlQrMerchant(accountNumber.removePrefix("bmlqr:"))
return@setFragmentResultListener
}
val label = bundle.getString(ContactPickerSheetFragment.KEY_LABEL) ?: ""
val subtitle = bundle.getString(ContactPickerSheetFragment.KEY_SUBTITLE) ?: accountNumber
val colorHex = bundle.getString(ContactPickerSheetFragment.KEY_COLOR) ?: "#607D8B"
@@ -292,6 +297,7 @@ class TransferFragment : Fragment() {
}
private fun lookupBmlQrMerchant(qrUrl: String) {
bmlQrLookupAttempted = true
bmlGatewayQr = qrUrl.startsWith("https://pay.bml.com.mv/app/")
val base64Url = android.util.Base64.encodeToString(
qrUrl.toByteArray(Charsets.UTF_8), android.util.Base64.NO_WRAP)
@@ -316,6 +322,16 @@ class TransferFragment : Fragment() {
return@launch
}
bmlQrInfo = info
if (info.amount == 0.0) {
RecentsCache.save(requireContext(), RecentPick(
accountNumber = "bmlqr:$qrUrl",
displayName = info.merchantName,
subtitle = info.merchantAddress.ifBlank { "BML Merchant" },
colorHex = "#0066A1",
imageHash = null,
isProfileImage = false
))
}
// Auto-select the user's default BML card if no card was pre-selected
if (selectedAccount == null) {
@@ -422,6 +438,14 @@ class TransferFragment : Fragment() {
updateTransferButton()
}
}
// On a cold start (e.g. share intent), anyBmlSession() may be null when
// onViewCreated runs. Retry the lookup once sessions are available.
val pendingBmlQrUrl = arguments?.getString(ARG_BML_QR_URL)
if (pendingBmlQrUrl != null && !bmlQrLookupAttempted) {
val app = requireActivity().application as BasedBankApp
if (app.anyBmlSession() != null) lookupBmlQrMerchant(pendingBmlQrUrl)
}
}
}
@@ -41,7 +41,7 @@ object PaymvQrParser {
PaymvQrData(
accountNumber = merchantInfo?.get("03"),
amount = root["54"],
amount = root["54"]?.takeIf { it != "***" },
purpose = additionalData?.get("08"),
merchantName = root["59"]
)
+9 -9
View File
@@ -26,15 +26,6 @@
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent">
<ImageButton
android:id="@+id/btnTransferContact"
android:layout_width="36dp"
android:layout_height="36dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_send"
android:padding="6dp"
android:contentDescription="@string/transfer" />
<ImageButton
android:id="@+id/btnEditContact"
android:layout_width="36dp"
@@ -54,6 +45,15 @@
android:tint="?attr/colorError"
android:contentDescription="@string/contact_delete" />
<ImageButton
android:id="@+id/btnTransferContact"
android:layout_width="36dp"
android:layout_height="36dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:src="@drawable/ic_send"
android:padding="6dp"
android:contentDescription="@string/transfer" />
</LinearLayout>
<LinearLayout