remove bml linked cards from account ui, trasfer drop down and added a cards section to show master cards

This commit is contained in:
2026-05-14 05:24:29 +05:00
parent e4684ec017
commit 3ddf687c8b
3 changed files with 203 additions and 27 deletions
@@ -185,25 +185,66 @@ class BmlLoginFlow {
val root = JSONObject(json) val root = JSONObject(json)
if (!root.optBoolean("success")) return emptyList() if (!root.optBoolean("success")) return emptyList()
val dashboard = root.optJSONObject("payload")?.optJSONArray("dashboard") ?: return emptyList() val dashboard = root.optJSONObject("payload")?.optJSONArray("dashboard") ?: return emptyList()
return (0 until dashboard.length()).map { i ->
val casaAccounts = mutableListOf<MibAccount>()
val seenPrepaid = mutableSetOf<String>()
val prepaidCards = mutableListOf<MibAccount>()
for (i in 0 until dashboard.length()) {
val item = dashboard.getJSONObject(i) val item = dashboard.getJSONObject(i)
val currency = item.optString("currency", "MVR") val currency = item.optString("currency", "MVR")
val available = item.optDouble("availableBalance", 0.0) val accountType = item.optString("account_type", "CASA")
MibAccount( val product = item.optString("product")
profileName = "Bank of Maldives", val accountNumber = item.optString("account")
profileType = "BML", val status = item.optString("account_status", "Active")
accountNumber = item.optString("account"),
accountBriefName = item.optString("alias"), if (accountType == "CASA") {
currencyName = currency, val available = item.optDouble("availableBalance", 0.0)
accountTypeName = item.optString("product"), casaAccounts.add(MibAccount(
availableBalance = "%.2f".format(available), profileName = "Bank of Maldives",
currentBalance = "%.2f".format(item.optDouble("ledgerBalance", 0.0)), profileType = "BML",
blockedAmount = "%.2f".format(item.optDouble("lockedAmount", 0.0)), accountNumber = accountNumber,
mvrBalance = if (currency == "MVR") "%.2f".format(available) else "0.00", accountBriefName = item.optString("alias"),
statusDesc = item.optString("account_status", "Active"), currencyName = currency,
profileImageHash = null accountTypeName = product,
) availableBalance = "%.2f".format(available),
currentBalance = "%.2f".format(item.optDouble("ledgerBalance", 0.0)),
blockedAmount = "%.2f".format(item.optDouble("lockedAmount", 0.0)),
mvrBalance = if (currency == "MVR") "%.2f".format(available) else "0.00",
statusDesc = status,
profileImageHash = null
))
} else if (accountType == "Card") {
val isPrepaid = item.optBoolean("prepaid_card", false)
if (isPrepaid) {
// Deduplicate by account number, prefer Active
if (!seenPrepaid.contains(accountNumber) || status == "Active") {
seenPrepaid.add(accountNumber)
prepaidCards.removeAll { it.accountNumber == accountNumber }
val cardBalance = item.optJSONObject("cardBalance")
val available = cardBalance?.optDouble("AvailableLimit", 0.0) ?: 0.0
prepaidCards.add(MibAccount(
profileName = "Cards",
profileType = "BML_PREPAID",
accountNumber = accountNumber,
accountBriefName = product,
currencyName = currency,
accountTypeName = product,
availableBalance = "%.2f".format(available),
currentBalance = "%.2f".format(cardBalance?.optDouble("CurrentBalance", 0.0) ?: 0.0),
blockedAmount = "0.00",
mvrBalance = if (currency == "MVR") "%.2f".format(available) else "0.00",
statusDesc = status,
profileImageHash = null
))
}
} else {
// Linked debit cards have no independent balance or account link — skip
}
}
} }
return casaAccounts + prepaidCards
} }
private fun parseContacts(json: String): List<MibBeneficiary> { private fun parseContacts(json: String): List<MibBeneficiary> {
@@ -1,10 +1,14 @@
package sh.sar.basedbank.ui.home package sh.sar.basedbank.ui.home
import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import sh.sar.basedbank.api.mib.MibAccount import sh.sar.basedbank.api.mib.MibAccount
import sh.sar.basedbank.databinding.ItemAccountBinding import sh.sar.basedbank.databinding.ItemAccountBinding
import sh.sar.basedbank.databinding.ItemCardBinding
import sh.sar.basedbank.databinding.ItemProfileHeaderBinding import sh.sar.basedbank.databinding.ItemProfileHeaderBinding
class AccountsAdapter(accounts: List<MibAccount>) : class AccountsAdapter(accounts: List<MibAccount>) :
@@ -13,6 +17,7 @@ class AccountsAdapter(accounts: List<MibAccount>) :
private sealed class Item { private sealed class Item {
data class Header(val profileName: String, val profileType: String) : Item() data class Header(val profileName: String, val profileType: String) : Item()
data class Account(val account: MibAccount) : Item() data class Account(val account: MibAccount) : Item()
data class Card(val account: MibAccount) : Item()
} }
private val items: MutableList<Item> = buildItems(accounts).toMutableList() private val items: MutableList<Item> = buildItems(accounts).toMutableList()
@@ -30,28 +35,34 @@ class AccountsAdapter(accounts: List<MibAccount>) :
add(Item.Header(account.profileName, account.profileType)) add(Item.Header(account.profileName, account.profileType))
lastProfile = account.profileName lastProfile = account.profileName
} }
add(Item.Account(account)) if (account.profileType == "BML_PREPAID") {
add(Item.Card(account))
} else {
add(Item.Account(account))
}
} }
} }
override fun getItemViewType(position: Int) = when (items[position]) { override fun getItemViewType(position: Int) = when (items[position]) {
is Item.Header -> TYPE_HEADER is Item.Header -> TYPE_HEADER
is Item.Account -> TYPE_ACCOUNT is Item.Account -> TYPE_ACCOUNT
is Item.Card -> TYPE_CARD
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context) val inflater = LayoutInflater.from(parent.context)
return if (viewType == TYPE_HEADER) { return when (viewType) {
HeaderViewHolder(ItemProfileHeaderBinding.inflate(inflater, parent, false)) TYPE_HEADER -> HeaderViewHolder(ItemProfileHeaderBinding.inflate(inflater, parent, false))
} else { TYPE_CARD -> CardViewHolder(ItemCardBinding.inflate(inflater, parent, false))
AccountViewHolder(ItemAccountBinding.inflate(inflater, parent, false)) else -> AccountViewHolder(ItemAccountBinding.inflate(inflater, parent, false))
} }
} }
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (val item = items[position]) { when (val item = items[position]) {
is Item.Header -> (holder as HeaderViewHolder).bind(item) is Item.Header -> (holder as HeaderViewHolder).bind(item)
is Item.Account -> (holder as AccountViewHolder).bind(item.account) is Item.Account -> (holder as AccountViewHolder).bind(item.account)
is Item.Card -> (holder as CardViewHolder).bind(item.account)
} }
} }
@@ -62,9 +73,10 @@ class AccountsAdapter(accounts: List<MibAccount>) :
fun bind(item: Item.Header) { fun bind(item: Item.Header) {
binding.tvProfileName.text = item.profileName binding.tvProfileName.text = item.profileName
binding.tvProfileType.text = when (item.profileType) { binding.tvProfileType.text = when (item.profileType) {
"BML" -> "Bank of Maldives" "BML" -> "Bank of Maldives"
"0" -> "Personal" "BML_PREPAID" -> "BML"
else -> "Business" "0" -> "Personal"
else -> "Business"
} }
} }
} }
@@ -80,8 +92,45 @@ class AccountsAdapter(accounts: List<MibAccount>) :
} }
} }
private inner class CardViewHolder(private val binding: ItemCardBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(account: MibAccount) {
val brand = cardBrand(account.accountTypeName)
binding.tvCardBrand.text = brand.label
setBrandBackground(brand.color)
binding.tvCardName.text = account.accountBriefName
binding.tvCardNumber.text = account.accountNumber
val isPrepaid = account.profileType == "BML_PREPAID"
binding.layoutCardBalance.visibility = if (isPrepaid) View.VISIBLE else View.GONE
if (isPrepaid) {
binding.tvCardBalance.text = "${account.currencyName} ${account.availableBalance}"
}
}
private fun setBrandBackground(colorHex: String) {
val drawable = GradientDrawable().apply {
shape = GradientDrawable.RECTANGLE
cornerRadius = 100f
setColor(Color.parseColor(colorHex))
}
binding.tvCardBrand.background = drawable
}
}
companion object { companion object {
private const val TYPE_HEADER = 0 private const val TYPE_HEADER = 0
private const val TYPE_ACCOUNT = 1 private const val TYPE_ACCOUNT = 1
private const val TYPE_CARD = 2
private data class Brand(val label: String, val color: String)
private fun cardBrand(productName: String): Brand = when {
productName.contains("AMEX", ignoreCase = true) ||
productName.contains("AMERICAN EXPRESS", ignoreCase = true) -> Brand("AMEX", "#016FD0")
productName.contains("VISA", ignoreCase = true) -> Brand("VISA", "#1A1F71")
productName.contains("MASTERCARD", ignoreCase = true) -> Brand("MC", "#FF5F00")
else -> Brand("CARD", "#555555")
}
} }
} }
+86
View File
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
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="wrap_content"
android:layout_marginBottom="12dp"
app:cardCornerRadius="16dp"
app:cardElevation="0dp"
app:strokeWidth="1dp"
app:strokeColor="?attr/colorOutlineVariant">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="20dp"
android:gravity="center_vertical">
<!-- Brand chip -->
<TextView
android:id="@+id/tvCardBrand"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingHorizontal="10dp"
android:paddingVertical="6dp"
android:layout_marginEnd="16dp"
android:textAppearance="?attr/textAppearanceLabelSmall"
android:textColor="@android:color/white"
android:fontFamily="monospace"
android:background="@drawable/chip_background" />
<!-- Card name + number -->
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/tvCardName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceTitleSmall"
android:textColor="?attr/colorOnSurface" />
<TextView
android:id="@+id/tvCardNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceBodySmall"
android:textColor="?attr/colorOnSurfaceVariant"
android:layout_marginTop="2dp"
android:fontFamily="monospace" />
</LinearLayout>
<!-- Balance (prepaid only) -->
<LinearLayout
android:id="@+id/layoutCardBalance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:gravity="end"
android:visibility="gone">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/available_balance"
android:textAppearance="?attr/textAppearanceLabelSmall"
android:textColor="?attr/colorOnSurfaceVariant" />
<TextView
android:id="@+id/tvCardBalance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?attr/textAppearanceTitleSmall"
android:textColor="?attr/colorOnSurface"
android:layout_marginTop="2dp" />
</LinearLayout>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>