redeisgn accounts page
All checks were successful
Auto Tag on Version Change / check-version (push) Successful in 3s

This commit is contained in:
2026-05-17 22:34:15 +05:00
parent 389344a192
commit cd4b3fef8b
9 changed files with 107 additions and 91 deletions

View File

@@ -34,6 +34,7 @@ class AccountHistoryAdapter(
private val iconUrlCache = mutableMapOf<String, Bitmap>()
var onImageNeeded: ((counterpartyName: String) -> Unit)? = null
var onIconUrlNeeded: ((url: String) -> Unit)? = null
var onTransferClick: ((MibAccount) -> Unit)? = null
fun updateImage(counterpartyName: String, bitmap: Bitmap) {
imageCache[counterpartyName] = bitmap
@@ -164,6 +165,7 @@ class AccountHistoryAdapter(
} else {
b.llHeaderBlocked.visibility = View.GONE
}
b.btnHeaderTransfer.setOnClickListener { onTransferClick?.invoke(acc) }
}
private fun friendlyType(raw: String): String {

View File

@@ -20,6 +20,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
import sh.sar.basedbank.BasedBankApp
import sh.sar.basedbank.R
import sh.sar.basedbank.api.bml.BmlLoginFlow
import sh.sar.basedbank.api.fahipay.FahipayLoginFlow
import sh.sar.basedbank.api.mib.MibAccount
@@ -82,6 +83,9 @@ class AccountHistoryFragment : Fragment() {
adapter = AccountHistoryAdapter(account)
adapter.onImageNeeded = { name -> loadContactImage(name) }
adapter.onIconUrlNeeded = { url -> loadMerchantIcon(url) }
adapter.onTransferClick = { acc ->
(activity as? HomeActivity)?.navigateTo(R.id.nav_transfer, TransferFragment.newInstanceFrom(acc))
}
binding.recyclerView.layoutManager = LinearLayoutManager(requireContext())
binding.recyclerView.adapter = adapter
binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {

View File

@@ -13,7 +13,7 @@ import androidx.recyclerview.widget.RecyclerView
import sh.sar.basedbank.api.mib.MibAccount
import sh.sar.basedbank.databinding.ItemAccountBinding
import sh.sar.basedbank.databinding.ItemCardBinding
import sh.sar.basedbank.databinding.ItemProfileHeaderBinding
import sh.sar.basedbank.databinding.ItemDateHeaderBinding
class AccountsAdapter(
accounts: List<MibAccount>,
@@ -21,7 +21,7 @@ class AccountsAdapter(
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private sealed class Item {
data class SectionTitle(val label: String, val chip: String) : Item()
data class SectionTitle(val label: String) : Item()
data class Account(val account: MibAccount) : Item()
data class Card(val account: MibAccount) : Item()
}
@@ -35,19 +35,40 @@ class AccountsAdapter(
}
private fun buildItems(accounts: List<MibAccount>): List<Item> = buildList {
val regular = accounts.filter { it.profileType != "BML_PREPAID" }
val prepaid = accounts.filter { it.profileType == "BML_PREPAID" }
val nonPrepaid = 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)) }
// Group non-prepaid accounts by their derived section title, preserving order
val groups = LinkedHashMap<String, MutableList<MibAccount>>()
for (acc in nonPrepaid) {
val title = sectionTitle(acc)
groups.getOrPut(title) { mutableListOf() }.add(acc)
}
for ((title, group) in groups) {
add(Item.SectionTitle(title))
group.forEach { add(Item.Account(it)) }
}
if (prepaid.isNotEmpty()) {
add(Item.SectionTitle("Cards", "BML"))
add(Item.SectionTitle("Cards · Bank of Maldives"))
prepaid.forEach { add(Item.Card(it)) }
}
}
private fun sectionTitle(account: MibAccount): String {
val profileLabel = when (account.profileType) {
"0" -> "Personal"
"1" -> "Business"
else -> account.profileName
}
val bank = when {
account.profileType.startsWith("BML") -> "Bank of Maldives"
account.profileType == "FAHIPAY" -> "Fahipay"
else -> "Maldives Islamic Bank"
}
return if (profileLabel.isNotBlank()) "$profileLabel · $bank" else bank
}
override fun getItemViewType(position: Int) = when (items[position]) {
is Item.SectionTitle -> TYPE_HEADER
is Item.Account -> TYPE_ACCOUNT
@@ -57,7 +78,7 @@ class AccountsAdapter(
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context)
return when (viewType) {
TYPE_HEADER -> SectionViewHolder(ItemProfileHeaderBinding.inflate(inflater, parent, false))
TYPE_HEADER -> SectionViewHolder(ItemDateHeaderBinding.inflate(inflater, parent, false))
TYPE_CARD -> CardViewHolder(ItemCardBinding.inflate(inflater, parent, false))
else -> AccountViewHolder(ItemAccountBinding.inflate(inflater, parent, false))
}
@@ -73,16 +94,10 @@ class AccountsAdapter(
override fun getItemCount() = items.size
private inner class SectionViewHolder(private val binding: ItemProfileHeaderBinding) :
private inner class SectionViewHolder(private val binding: ItemDateHeaderBinding) :
RecyclerView.ViewHolder(binding.root) {
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
}
binding.tvDateHeader.text = item.label
}
}
@@ -91,18 +106,8 @@ class AccountsAdapter(
fun bind(account: MibAccount) {
binding.tvAccountName.text = account.accountBriefName
binding.tvAccountNumber.text = account.accountNumber
binding.tvPillBank.text = when {
account.profileType.startsWith("BML") -> "BML"
account.profileType == "FAHIPAY" -> "FP"
else -> null
}
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.tvPillType.text = friendlyAccountType(account.accountTypeName)
binding.tvBalance.text = "${account.currencyName} ${account.availableBalance}"
binding.root.setOnClickListener { onAccountClick(account) }
binding.root.setOnLongClickListener {
copyToClipboard(it.context, account.accountNumber)

View File

@@ -158,7 +158,7 @@ class ContactsFragment : Fragment() {
colorHex = contact.bankColor,
imageHash = contact.customerImgHash
)
(requireActivity() as HomeActivity).showWithBackStack(fragment)
(requireActivity() as HomeActivity).navigateTo(R.id.nav_transfer, fragment)
}
private fun confirmDelete(contact: MibBeneficiary) {

View File

@@ -199,18 +199,19 @@ class HomeActivity : AppCompatActivity() {
}
}
fun navigateTo(itemId: Int) {
when (itemId) {
R.id.nav_dashboard -> show(DashboardFragment())
R.id.nav_accounts -> show(AccountsFragment())
R.id.nav_contacts -> show(ContactsFragment())
R.id.nav_transfer -> show(TransferFragment())
R.id.nav_transfer_history -> show(TransferHistoryFragment())
R.id.nav_finances -> show(FinancingFragment())
R.id.nav_otp -> show(OtpFragment())
R.id.nav_settings -> show(SettingsFragment())
else -> Toast.makeText(this, R.string.work_in_progress, Toast.LENGTH_SHORT).show()
fun navigateTo(itemId: Int, fragment: Fragment? = null) {
val dest = fragment ?: when (itemId) {
R.id.nav_dashboard -> DashboardFragment()
R.id.nav_accounts -> AccountsFragment()
R.id.nav_contacts -> ContactsFragment()
R.id.nav_transfer -> TransferFragment()
R.id.nav_transfer_history -> TransferHistoryFragment()
R.id.nav_finances -> FinancingFragment()
R.id.nav_otp -> OtpFragment()
R.id.nav_settings -> SettingsFragment()
else -> { Toast.makeText(this, R.string.work_in_progress, Toast.LENGTH_SHORT).show(); return }
}
show(dest)
binding.navigationView.setCheckedItem(itemId)
val bottomNavIds = setOf(R.id.nav_dashboard, R.id.nav_accounts, R.id.nav_contacts, R.id.nav_transfer, R.id.nav_more)
if (binding.bottomNavigation.visibility == View.VISIBLE && itemId in bottomNavIds) {

View File

@@ -93,6 +93,11 @@ class TransferFragment : Fragment() {
private const val ARG_SUBTITLE = "contact_subtitle"
private const val ARG_COLOR = "contact_color"
private const val ARG_IMAGE_HASH = "contact_image_hash"
private const val ARG_FROM_ACCOUNT = "from_account"
fun newInstanceFrom(account: MibAccount) = TransferFragment().apply {
arguments = Bundle().apply { putString(ARG_FROM_ACCOUNT, account.accountNumber) }
}
fun newInstance(
accountNumber: String,
@@ -171,6 +176,16 @@ class TransferFragment : Fragment() {
updateAmountPrefix(picked)
showFromCard(picked)
}
val fromNumber = arguments?.getString(ARG_FROM_ACCOUNT)
if (fromNumber != null && selectedAccount == null) {
val match = accounts.firstOrNull { it.accountNumber == fromNumber }
if (match != null) {
selectedAccount = match
updateAmountPrefix(match)
showFromCard(match)
}
}
}
}

View File

@@ -1,20 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
<LinearLayout
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">
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="20dp"
android:paddingHorizontal="16dp"
android:paddingVertical="14dp"
android:gravity="center_vertical"
android:foreground="?attr/selectableItemBackground">
@@ -43,7 +39,7 @@
</LinearLayout>
<!-- Right: segmented pill (bank | type | profile) + balance -->
<!-- Right: segmented pill (bank | type) + balance -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -58,41 +54,11 @@
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:paddingStart="12dp"
android:paddingEnd="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/tvPillType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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:paddingHorizontal="12dp"
android:paddingVertical="6dp"
android:textAppearance="?attr/textAppearanceLabelSmall"
android:textColor="?attr/colorOnSurface" />
@@ -111,4 +77,10 @@
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginHorizontal="16dp"
android:background="?attr/colorOutlineVariant" />
</LinearLayout>

View File

@@ -92,6 +92,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="16dp">
<LinearLayout
@@ -165,6 +166,20 @@
</LinearLayout>
<com.google.android.material.button.MaterialButton
android:id="@+id/btnHeaderTransfer"
style="@style/Widget.Material3.Button.IconButton"
android:layout_width="40dp"
android:layout_height="40dp"
android:insetTop="0dp"
android:insetBottom="0dp"
android:minWidth="0dp"
android:minHeight="0dp"
app:icon="@drawable/ic_send"
app:iconSize="20dp"
app:iconGravity="textStart"
app:iconPadding="0dp" />
</LinearLayout>
</LinearLayout>

View File

@@ -1,20 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
<LinearLayout
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">
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="20dp"
android:paddingHorizontal="16dp"
android:paddingVertical="14dp"
android:gravity="center_vertical"
android:foreground="?attr/selectableItemBackground">
@@ -89,4 +85,10 @@
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginHorizontal="16dp"
android:background="?attr/colorOutlineVariant" />
</LinearLayout>