redesign accounts page ui
This commit is contained in:
@@ -187,7 +187,6 @@ class BmlLoginFlow {
|
||||
val dashboard = root.optJSONObject("payload")?.optJSONArray("dashboard") ?: return emptyList()
|
||||
|
||||
val casaAccounts = mutableListOf<MibAccount>()
|
||||
val seenPrepaid = mutableSetOf<String>()
|
||||
val prepaidCards = mutableListOf<MibAccount>()
|
||||
|
||||
for (i in 0 until dashboard.length()) {
|
||||
@@ -201,7 +200,7 @@ class BmlLoginFlow {
|
||||
if (accountType == "CASA") {
|
||||
val available = item.optDouble("availableBalance", 0.0)
|
||||
casaAccounts.add(MibAccount(
|
||||
profileName = "Bank of Maldives",
|
||||
profileName = "Personal",
|
||||
profileType = "BML",
|
||||
accountNumber = accountNumber,
|
||||
accountBriefName = item.optString("alias"),
|
||||
@@ -217,27 +216,22 @@ class BmlLoginFlow {
|
||||
} 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
|
||||
))
|
||||
}
|
||||
val cardBalance = item.optJSONObject("cardBalance")
|
||||
val available = cardBalance?.optDouble("AvailableLimit", 0.0) ?: 0.0
|
||||
prepaidCards.add(MibAccount(
|
||||
profileName = "Personal",
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
package sh.sar.basedbank.ui.home
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import sh.sar.basedbank.api.mib.MibAccount
|
||||
import sh.sar.basedbank.databinding.ItemAccountBinding
|
||||
@@ -15,7 +19,7 @@ class AccountsAdapter(accounts: List<MibAccount>) :
|
||||
RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
|
||||
private sealed class Item {
|
||||
data class Header(val profileName: String, val profileType: String) : Item()
|
||||
data class SectionTitle(val label: String, val chip: String) : Item()
|
||||
data class Account(val account: MibAccount) : Item()
|
||||
data class Card(val account: MibAccount) : Item()
|
||||
}
|
||||
@@ -29,30 +33,29 @@ class AccountsAdapter(accounts: List<MibAccount>) :
|
||||
}
|
||||
|
||||
private fun buildItems(accounts: List<MibAccount>): List<Item> = buildList {
|
||||
var lastProfile = ""
|
||||
for (account in accounts) {
|
||||
if (account.profileName != lastProfile) {
|
||||
add(Item.Header(account.profileName, account.profileType))
|
||||
lastProfile = account.profileName
|
||||
}
|
||||
if (account.profileType == "BML_PREPAID") {
|
||||
add(Item.Card(account))
|
||||
} else {
|
||||
add(Item.Account(account))
|
||||
}
|
||||
val regular = accounts.filter { it.profileType != "BML_PREPAID" }
|
||||
val prepaid = accounts.filter { it.profileType == "BML_PREPAID" }
|
||||
|
||||
if (regular.isNotEmpty()) {
|
||||
add(Item.SectionTitle("Accounts", ""))
|
||||
regular.forEach { add(Item.Account(it)) }
|
||||
}
|
||||
if (prepaid.isNotEmpty()) {
|
||||
add(Item.SectionTitle("Cards", "BML"))
|
||||
prepaid.forEach { add(Item.Card(it)) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int) = when (items[position]) {
|
||||
is Item.Header -> TYPE_HEADER
|
||||
is Item.Account -> TYPE_ACCOUNT
|
||||
is Item.Card -> TYPE_CARD
|
||||
is Item.SectionTitle -> TYPE_HEADER
|
||||
is Item.Account -> TYPE_ACCOUNT
|
||||
is Item.Card -> TYPE_CARD
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val inflater = LayoutInflater.from(parent.context)
|
||||
return when (viewType) {
|
||||
TYPE_HEADER -> HeaderViewHolder(ItemProfileHeaderBinding.inflate(inflater, parent, false))
|
||||
TYPE_HEADER -> SectionViewHolder(ItemProfileHeaderBinding.inflate(inflater, parent, false))
|
||||
TYPE_CARD -> CardViewHolder(ItemCardBinding.inflate(inflater, parent, false))
|
||||
else -> AccountViewHolder(ItemAccountBinding.inflate(inflater, parent, false))
|
||||
}
|
||||
@@ -60,23 +63,23 @@ class AccountsAdapter(accounts: List<MibAccount>) :
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (val item = items[position]) {
|
||||
is Item.Header -> (holder as HeaderViewHolder).bind(item)
|
||||
is Item.Account -> (holder as AccountViewHolder).bind(item.account)
|
||||
is Item.Card -> (holder as CardViewHolder).bind(item.account)
|
||||
is Item.SectionTitle -> (holder as SectionViewHolder).bind(item)
|
||||
is Item.Account -> (holder as AccountViewHolder).bind(item.account)
|
||||
is Item.Card -> (holder as CardViewHolder).bind(item.account)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = items.size
|
||||
|
||||
private inner class HeaderViewHolder(private val binding: ItemProfileHeaderBinding) :
|
||||
private inner class SectionViewHolder(private val binding: ItemProfileHeaderBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(item: Item.Header) {
|
||||
binding.tvProfileName.text = item.profileName
|
||||
binding.tvProfileType.text = when (item.profileType) {
|
||||
"BML" -> "Bank of Maldives"
|
||||
"BML_PREPAID" -> "BML"
|
||||
"0" -> "Personal"
|
||||
else -> "Business"
|
||||
fun bind(item: Item.SectionTitle) {
|
||||
binding.tvProfileName.text = item.label
|
||||
if (item.chip.isNotEmpty()) {
|
||||
binding.tvProfileType.text = item.chip
|
||||
binding.tvProfileType.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.tvProfileType.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,11 +87,20 @@ class AccountsAdapter(accounts: List<MibAccount>) :
|
||||
private inner class AccountViewHolder(private val binding: ItemAccountBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
fun bind(account: MibAccount) {
|
||||
binding.tvAccountName.text = account.accountBriefName
|
||||
binding.tvAccountName.text = account.accountBriefName
|
||||
binding.tvAccountNumber.text = account.accountNumber
|
||||
binding.tvPillBank.text = if (account.profileType.startsWith("BML")) "BML" else "MIB"
|
||||
binding.tvPillType.text = friendlyAccountType(account.accountTypeName)
|
||||
binding.tvPillProfile.text = when (account.profileType) {
|
||||
"0" -> "Personal"
|
||||
"1" -> "Business"
|
||||
else -> account.profileName
|
||||
}
|
||||
binding.tvBalance.text = "${account.currencyName} ${account.availableBalance}"
|
||||
binding.tvAccountType.text = account.accountTypeName
|
||||
binding.tvStatus.text = account.statusDesc
|
||||
binding.root.setOnLongClickListener {
|
||||
copyToClipboard(it.context, account.accountNumber)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,24 +109,25 @@ class AccountsAdapter(accounts: List<MibAccount>) :
|
||||
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 {
|
||||
binding.tvCardBrand.background = GradientDrawable().apply {
|
||||
shape = GradientDrawable.RECTANGLE
|
||||
cornerRadius = 100f
|
||||
setColor(Color.parseColor(colorHex))
|
||||
setColor(Color.parseColor(brand.color))
|
||||
}
|
||||
binding.tvCardName.text = account.accountBriefName
|
||||
binding.tvCardNumber.text = account.accountNumber
|
||||
binding.layoutCardBalance.visibility = View.VISIBLE
|
||||
binding.tvCardBalance.text = "${account.currencyName} ${account.availableBalance}"
|
||||
|
||||
val isActive = account.statusDesc.equals("Active", ignoreCase = true)
|
||||
if (isActive) {
|
||||
binding.tvCardStatus.visibility = View.GONE
|
||||
binding.root.alpha = 1f
|
||||
} else {
|
||||
binding.tvCardStatus.text = account.statusDesc
|
||||
binding.tvCardStatus.visibility = View.VISIBLE
|
||||
binding.root.alpha = 0.45f
|
||||
}
|
||||
binding.tvCardBrand.background = drawable
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,6 +136,27 @@ class AccountsAdapter(accounts: List<MibAccount>) :
|
||||
private const val TYPE_ACCOUNT = 1
|
||||
private const val TYPE_CARD = 2
|
||||
|
||||
private fun copyToClipboard(context: Context, accountNumber: String) {
|
||||
val cm = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
cm.setPrimaryClip(ClipData.newPlainText("Account Number", accountNumber))
|
||||
Toast.makeText(context, "Account number copied", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
private fun friendlyAccountType(raw: String): String {
|
||||
val u = raw.trim().uppercase()
|
||||
return when {
|
||||
u == "SAVINGS ACCOUNT" ||
|
||||
u == "SAVING ACCOUNT" -> "Savings"
|
||||
u == "CURRENT ACCOUNT" ||
|
||||
u == "CURRENT ACCOUNT(PERSONAL)" ||
|
||||
u == "CURRENT ACCOUNT(BUSINESS)" -> "Current"
|
||||
u == "WADIAH RETAIL CURRENT ACCOUNT" ||
|
||||
u == "WADIAH BUSINESS CURRENT ACCOUNT" -> "Islamic Current"
|
||||
u == "BML ISLAMIC SAVINGS ACCOUNT" -> "Islamic Savings"
|
||||
else -> raw.trim()
|
||||
}
|
||||
}
|
||||
|
||||
private data class Brand(val label: String, val color: String)
|
||||
|
||||
private fun cardBrand(productName: String): Brand = when {
|
||||
|
||||
6
app/src/main/res/drawable/pill_segment_bg.xml
Normal file
6
app/src/main/res/drawable/pill_segment_bg.xml
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="100dp" />
|
||||
<stroke android:width="1dp" android:color="?attr/colorOutline" />
|
||||
</shape>
|
||||
@@ -13,92 +13,98 @@
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="20dp">
|
||||
android:orientation="horizontal"
|
||||
android:padding="20dp"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<!-- Left: name / number -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginBottom="12dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAccountName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
||||
android:textColor="?attr/colorOnSurface" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAccountNumber"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
android:layout_marginTop="2dp" />
|
||||
|
||||
</LinearLayout>
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:id="@+id/tvAccountName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingVertical="4dp"
|
||||
android:background="@drawable/chip_background"
|
||||
android:textAppearance="?attr/textAppearanceLabelSmall"
|
||||
android:textColor="?attr/colorOnSecondaryContainer" />
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
android:textColor="?attr/colorOnSurface" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAccountNumber"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
android:fontFamily="monospace"
|
||||
android:layout_marginTop="2dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="?attr/colorOutlineVariant"
|
||||
android:layout_marginBottom="12dp" />
|
||||
|
||||
<!-- Right: segmented pill (bank | type | profile) + balance -->
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
android:orientation="vertical"
|
||||
android:gravity="end"
|
||||
android:layout_marginStart="16dp">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical"
|
||||
android:background="@drawable/pill_segment_bg">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPillBank"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/available_balance"
|
||||
android:paddingStart="12dp"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingVertical="6dp"
|
||||
android:textAppearance="?attr/textAppearanceLabelSmall"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
android:textColor="?attr/colorOnSurface" />
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="16dp"
|
||||
android:background="?attr/colorOutline" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvBalance"
|
||||
android:id="@+id/tvPillType"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
android:layout_marginTop="2dp" />
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingVertical="6dp"
|
||||
android:textAppearance="?attr/textAppearanceLabelSmall"
|
||||
android:textColor="?attr/colorOnSurface" />
|
||||
|
||||
<View
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="16dp"
|
||||
android:background="?attr/colorOutline" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvPillProfile"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="10dp"
|
||||
android:paddingEnd="12dp"
|
||||
android:paddingVertical="6dp"
|
||||
android:textAppearance="?attr/textAppearanceLabelSmall"
|
||||
android:textColor="?attr/colorOnSurface" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvAccountType"
|
||||
android:id="@+id/tvBalance"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
android:layout_marginTop="6dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -41,35 +41,40 @@
|
||||
android:id="@+id/tvCardName"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
||||
android:textAppearance="?attr/textAppearanceTitleMedium"
|
||||
android:textColor="?attr/colorOnSurface" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCardNumber"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
android:layout_marginTop="2dp"
|
||||
android:fontFamily="monospace" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<!-- Balance (prepaid only) -->
|
||||
<!-- Status pill + 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:layout_marginStart="16dp"
|
||||
android:visibility="gone">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCardStatus"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/available_balance"
|
||||
android:paddingHorizontal="8dp"
|
||||
android:paddingVertical="3dp"
|
||||
android:textAppearance="?attr/textAppearanceLabelSmall"
|
||||
android:textColor="?attr/colorOnSurfaceVariant" />
|
||||
android:textColor="?attr/colorOnSurfaceVariant"
|
||||
android:background="@drawable/pill_segment_bg"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvCardBalance"
|
||||
@@ -77,7 +82,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?attr/textAppearanceTitleSmall"
|
||||
android:textColor="?attr/colorOnSurface"
|
||||
android:layout_marginTop="2dp" />
|
||||
android:layout_marginTop="6dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user