diff --git a/app/src/main/assets/cards/bml/amex_credit_gold.png b/app/src/main/assets/cards/bml/amex_credit_gold.png new file mode 100644 index 0000000..7b694a4 Binary files /dev/null and b/app/src/main/assets/cards/bml/amex_credit_gold.png differ diff --git a/app/src/main/assets/cards/bml/amex_credit_green.png b/app/src/main/assets/cards/bml/amex_credit_green.png new file mode 100644 index 0000000..e7901a8 Binary files /dev/null and b/app/src/main/assets/cards/bml/amex_credit_green.png differ diff --git a/app/src/main/assets/cards/bml/amex_debit_gold.png b/app/src/main/assets/cards/bml/amex_debit_gold.png new file mode 100644 index 0000000..e60cfad Binary files /dev/null and b/app/src/main/assets/cards/bml/amex_debit_gold.png differ diff --git a/app/src/main/assets/cards/bml/amex_debit_green.png b/app/src/main/assets/cards/bml/amex_debit_green.png new file mode 100644 index 0000000..90b696a Binary files /dev/null and b/app/src/main/assets/cards/bml/amex_debit_green.png differ diff --git a/app/src/main/assets/cards/bml/amex_platinum.png b/app/src/main/assets/cards/bml/amex_platinum.png new file mode 100644 index 0000000..7802b06 Binary files /dev/null and b/app/src/main/assets/cards/bml/amex_platinum.png differ diff --git a/app/src/main/assets/cards/bml/defaultcard.png b/app/src/main/assets/cards/bml/defaultcard.png new file mode 100644 index 0000000..66a8672 Binary files /dev/null and b/app/src/main/assets/cards/bml/defaultcard.png differ diff --git a/app/src/main/assets/cards/bml/master.png b/app/src/main/assets/cards/bml/master.png new file mode 100644 index 0000000..fd8cb45 Binary files /dev/null and b/app/src/main/assets/cards/bml/master.png differ diff --git a/app/src/main/assets/cards/bml/master_business_debit.png b/app/src/main/assets/cards/bml/master_business_debit.png new file mode 100644 index 0000000..f1acba3 Binary files /dev/null and b/app/src/main/assets/cards/bml/master_business_debit.png differ diff --git a/app/src/main/assets/cards/bml/master_gold.png b/app/src/main/assets/cards/bml/master_gold.png new file mode 100644 index 0000000..eac1d10 Binary files /dev/null and b/app/src/main/assets/cards/bml/master_gold.png differ diff --git a/app/src/main/assets/cards/bml/master_islamic.png b/app/src/main/assets/cards/bml/master_islamic.png new file mode 100644 index 0000000..ce266e6 Binary files /dev/null and b/app/src/main/assets/cards/bml/master_islamic.png differ diff --git a/app/src/main/assets/cards/bml/master_masveriyaa.png b/app/src/main/assets/cards/bml/master_masveriyaa.png new file mode 100644 index 0000000..0f6106f Binary files /dev/null and b/app/src/main/assets/cards/bml/master_masveriyaa.png differ diff --git a/app/src/main/assets/cards/bml/master_odiveriyaa.png b/app/src/main/assets/cards/bml/master_odiveriyaa.png new file mode 100644 index 0000000..0645ef2 Binary files /dev/null and b/app/src/main/assets/cards/bml/master_odiveriyaa.png differ diff --git a/app/src/main/assets/cards/bml/master_passport.png b/app/src/main/assets/cards/bml/master_passport.png new file mode 100644 index 0000000..553eb77 Binary files /dev/null and b/app/src/main/assets/cards/bml/master_passport.png differ diff --git a/app/src/main/assets/cards/bml/master_platinum.png b/app/src/main/assets/cards/bml/master_platinum.png new file mode 100644 index 0000000..6458cbd Binary files /dev/null and b/app/src/main/assets/cards/bml/master_platinum.png differ diff --git a/app/src/main/assets/cards/bml/master_prepaid.png b/app/src/main/assets/cards/bml/master_prepaid.png new file mode 100644 index 0000000..a652fd3 Binary files /dev/null and b/app/src/main/assets/cards/bml/master_prepaid.png differ diff --git a/app/src/main/assets/cards/bml/master_prepaid_business.png b/app/src/main/assets/cards/bml/master_prepaid_business.png new file mode 100644 index 0000000..0c829eb Binary files /dev/null and b/app/src/main/assets/cards/bml/master_prepaid_business.png differ diff --git a/app/src/main/assets/cards/bml/master_prepaid_travel.png b/app/src/main/assets/cards/bml/master_prepaid_travel.png new file mode 100644 index 0000000..3a983c3 Binary files /dev/null and b/app/src/main/assets/cards/bml/master_prepaid_travel.png differ diff --git a/app/src/main/assets/cards/bml/master_world.png b/app/src/main/assets/cards/bml/master_world.png new file mode 100644 index 0000000..64cb95b Binary files /dev/null and b/app/src/main/assets/cards/bml/master_world.png differ diff --git a/app/src/main/assets/cards/bml/visa_corporate.png b/app/src/main/assets/cards/bml/visa_corporate.png new file mode 100644 index 0000000..997be64 Binary files /dev/null and b/app/src/main/assets/cards/bml/visa_corporate.png differ diff --git a/app/src/main/assets/cards/bml/visa_credit.png b/app/src/main/assets/cards/bml/visa_credit.png new file mode 100644 index 0000000..edbe23c Binary files /dev/null and b/app/src/main/assets/cards/bml/visa_credit.png differ diff --git a/app/src/main/assets/cards/bml/visa_debit.png b/app/src/main/assets/cards/bml/visa_debit.png new file mode 100644 index 0000000..00b1742 Binary files /dev/null and b/app/src/main/assets/cards/bml/visa_debit.png differ diff --git a/app/src/main/assets/cards/bml/visa_debit_generic.png b/app/src/main/assets/cards/bml/visa_debit_generic.png new file mode 100644 index 0000000..0e3b815 Binary files /dev/null and b/app/src/main/assets/cards/bml/visa_debit_generic.png differ diff --git a/app/src/main/assets/cards/bml/visa_debit_islamic.png b/app/src/main/assets/cards/bml/visa_debit_islamic.png new file mode 100644 index 0000000..6e308cd Binary files /dev/null and b/app/src/main/assets/cards/bml/visa_debit_islamic.png differ diff --git a/app/src/main/assets/cards/bml/visa_debit_platinum.png b/app/src/main/assets/cards/bml/visa_debit_platinum.png new file mode 100644 index 0000000..50d1a59 Binary files /dev/null and b/app/src/main/assets/cards/bml/visa_debit_platinum.png differ diff --git a/app/src/main/assets/cards/bml/visa_gold.png b/app/src/main/assets/cards/bml/visa_gold.png new file mode 100644 index 0000000..07ecf35 Binary files /dev/null and b/app/src/main/assets/cards/bml/visa_gold.png differ diff --git a/app/src/main/assets/cards/bml/visa_infinite.png b/app/src/main/assets/cards/bml/visa_infinite.png new file mode 100644 index 0000000..70be0ef Binary files /dev/null and b/app/src/main/assets/cards/bml/visa_infinite.png differ diff --git a/app/src/main/assets/cards/bml/visa_platinum.png b/app/src/main/assets/cards/bml/visa_platinum.png new file mode 100644 index 0000000..1a23bce Binary files /dev/null and b/app/src/main/assets/cards/bml/visa_platinum.png differ diff --git a/app/src/main/assets/cards/bml/visa_student_black.png b/app/src/main/assets/cards/bml/visa_student_black.png new file mode 100644 index 0000000..d21b439 Binary files /dev/null and b/app/src/main/assets/cards/bml/visa_student_black.png differ diff --git a/app/src/main/assets/cards/bml/visa_student_blue.png b/app/src/main/assets/cards/bml/visa_student_blue.png new file mode 100644 index 0000000..b03ec46 Binary files /dev/null and b/app/src/main/assets/cards/bml/visa_student_blue.png differ diff --git a/app/src/main/assets/cards/mib/visa_black_platinum.jpg b/app/src/main/assets/cards/mib/visa_black_platinum.jpg deleted file mode 100644 index ad1c19f..0000000 Binary files a/app/src/main/assets/cards/mib/visa_black_platinum.jpg and /dev/null differ diff --git a/app/src/main/assets/cards/mib/visa_black_platinum.png b/app/src/main/assets/cards/mib/visa_black_platinum.png new file mode 100644 index 0000000..b3d7b0d Binary files /dev/null and b/app/src/main/assets/cards/mib/visa_black_platinum.png differ diff --git a/app/src/main/assets/cards/mib/visa_blue_everyday.jpg b/app/src/main/assets/cards/mib/visa_blue_everyday.jpg deleted file mode 100644 index 765c3af..0000000 Binary files a/app/src/main/assets/cards/mib/visa_blue_everyday.jpg and /dev/null differ diff --git a/app/src/main/assets/cards/mib/visa_blue_everyday.png b/app/src/main/assets/cards/mib/visa_blue_everyday.png new file mode 100644 index 0000000..e7bb290 Binary files /dev/null and b/app/src/main/assets/cards/mib/visa_blue_everyday.png differ diff --git a/app/src/main/assets/cards/mib/visa_business.jpg b/app/src/main/assets/cards/mib/visa_business.jpg deleted file mode 100644 index 7fc6a1e..0000000 Binary files a/app/src/main/assets/cards/mib/visa_business.jpg and /dev/null differ diff --git a/app/src/main/assets/cards/mib/visa_business.png b/app/src/main/assets/cards/mib/visa_business.png new file mode 100644 index 0000000..b9e5c4a Binary files /dev/null and b/app/src/main/assets/cards/mib/visa_business.png differ diff --git a/app/src/main/java/sh/sar/basedbank/api/bml/BmlAccountClient.kt b/app/src/main/java/sh/sar/basedbank/api/bml/BmlAccountClient.kt index 800d357..c3abe8a 100644 --- a/app/src/main/java/sh/sar/basedbank/api/bml/BmlAccountClient.kt +++ b/app/src/main/java/sh/sar/basedbank/api/bml/BmlAccountClient.kt @@ -166,16 +166,21 @@ class BmlAccountClient { internalId = internalId )) } else if (accountType == "Card") { - val isVisible = item.optBoolean("account_visible", false) - if (!isVisible) continue val isPrepaid = item.optBoolean("prepaid_card", false) + val productCode = item.optString("product_code", "") val cardBalance = item.optJSONObject("cardBalance") val available = cardBalance?.optDouble("AvailableLimit", 0.0) ?: 0.0 val current = cardBalance?.optDouble("CurrentBalance", 0.0) ?: 0.0 + val cardProfileType = when { + isPrepaid -> "BML_PREPAID" + product.contains("CREDIT", ignoreCase = true) -> "BML_CREDIT" + else -> "BML_DEBIT" + } prepaidCards.add(BankAccount( bank = "BML", profileName = profileName, - profileType = if (isPrepaid) "BML_PREPAID" else "BML_CREDIT", + profileType = cardProfileType, + cifType = productCode, accountNumber = accountNumber, accountBriefName = item.optString("alias").ifBlank { product }, currencyName = currency, diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/CardSettingsFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/CardSettingsFragment.kt index 51d2fe3..d2dc076 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/CardSettingsFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/CardSettingsFragment.kt @@ -15,8 +15,8 @@ import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import sh.sar.basedbank.R -import sh.sar.basedbank.api.mib.MibCard import sh.sar.basedbank.databinding.FragmentCardSettingsBinding +import sh.sar.basedbank.util.bmlapi.BmlCardParser class CardSettingsFragment : Fragment() { @@ -44,17 +44,23 @@ class CardSettingsFragment : Fragment() { } binding.swipeRefresh.setOnRefreshListener { - (activity as? HomeActivity)?.triggerRefreshCards() + (activity as? HomeActivity)?.triggerRefresh() } - viewModel.mibCards.observe(viewLifecycleOwner) { cards -> - if (cards == null) return@observe - adapter.update(cards) + val updateCardList = { + val mibItems = (viewModel.mibCards.value ?: emptyList()).map { CardItem.Mib(it) } + val bmlItems = (viewModel.accounts.value ?: emptyList()) + .filter { it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT" } + .map { CardItem.Bml(it) } + val all = mibItems + bmlItems + adapter.update(all) binding.loadingView.visibility = View.GONE binding.swipeRefresh.isRefreshing = false - binding.emptyView.visibility = if (cards.isEmpty()) View.VISIBLE else View.GONE - binding.recyclerView.visibility = if (cards.isEmpty()) View.GONE else View.VISIBLE + binding.emptyView.visibility = if (all.isEmpty()) View.VISIBLE else View.GONE + binding.recyclerView.visibility = if (all.isEmpty()) View.GONE else View.VISIBLE } + viewModel.mibCards.observe(viewLifecycleOwner) { updateCardList() } + viewModel.accounts.observe(viewLifecycleOwner) { updateCardList() } if (viewModel.mibCards.value == null) { binding.loadingView.visibility = View.VISIBLE @@ -73,11 +79,11 @@ class CardSettingsFragment : Fragment() { } private inner class CardSettingsAdapter( - private var cards: List, + private var cards: List, private val context: Context ) : RecyclerView.Adapter() { - fun update(newCards: List) { + fun update(newCards: List) { cards = newCards notifyDataSetChanged() } @@ -93,17 +99,34 @@ class CardSettingsFragment : Fragment() { private val tvCardOwner: TextView = view.findViewById(R.id.tvCardOwner) private val tvCardNumber: TextView = view.findViewById(R.id.tvCardNumber) private val tvCardType: TextView = view.findViewById(R.id.tvCardType) + private val tvCardStatus: TextView = view.findViewById(R.id.tvCardStatus) private val btnChangePin: View = view.findViewById(R.id.btnChangePin) private val btnFreeze: View = view.findViewById(R.id.btnFreeze) private val btnBlock: View = view.findViewById(R.id.btnBlock) - fun bind(card: MibCard) { - tvCardOwner.text = card.cardHolderName - tvCardNumber.text = PayWithCardFragment.formatMasked(card.maskedCardNumber) - tvCardType.text = card.cardTypeDesc - val assetPath = PayWithCardFragment.cardImageAsset(card) - if (assetPath != null) PayWithCardFragment.loadCardImage(ivCardImage, assetPath) - else ivCardImage.setImageDrawable(null) + fun bind(item: CardItem) { + when (item) { + is CardItem.Mib -> { + tvCardOwner.text = item.card.cardHolderName + tvCardNumber.text = PayWithCardFragment.formatMasked(item.card.maskedCardNumber) + tvCardType.text = item.card.cardTypeDesc + val assetPath = PayWithCardFragment.cardImageAsset(item.card) + if (assetPath != null) PayWithCardFragment.loadCardImage(ivCardImage, assetPath) + else ivCardImage.setImageDrawable(null) + PayWithCardFragment.bindCardStatus(tvCardStatus, PayWithCardFragment.mibCardStatusLabel(item.card.cardStatus)) + itemView.alpha = 1f + } + is CardItem.Bml -> { + tvCardOwner.text = item.account.accountBriefName + tvCardNumber.text = PayWithCardFragment.formatMasked(item.account.accountNumber) + tvCardType.text = item.account.accountTypeName + PayWithCardFragment.loadCardImage(ivCardImage, BmlCardParser.cardImageAsset(item.account)) + val isActive = item.account.statusDesc.equals("Active", ignoreCase = true) + val bmlStatus = item.account.statusDesc.takeUnless { isActive } + PayWithCardFragment.bindCardStatus(tvCardStatus, bmlStatus) + itemView.alpha = if (isActive) 1f else 0.45f + } + } val wip = View.OnClickListener { Toast.makeText(context, R.string.work_in_progress, Toast.LENGTH_SHORT).show() } diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/ContactPickerSheetFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/ContactPickerSheetFragment.kt index 41be3ba..5d556ca 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/ContactPickerSheetFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/ContactPickerSheetFragment.kt @@ -209,11 +209,11 @@ class ContactPickerSheetFragment : BottomSheetDialogFragment() { val fromAccount = accounts.find { it.accountNumber == fromAccountNumber } val fromCurrency = fromAccount?.currencyName ?: "" val fromLoginTag = fromAccount?.loginTag ?: "" - val fromIsCard = fromAccount?.profileType == "BML_PREPAID" || fromAccount?.profileType == "BML_CREDIT" + val fromIsCard = fromAccount?.profileType == "BML_PREPAID" || fromAccount?.profileType == "BML_CREDIT" || fromAccount?.profileType == "BML_DEBIT" if (tabTag == MY_ACCOUNTS_TAG) { - val regularAccounts = accounts.filter { it.profileType != "BML_PREPAID" && it.profileType != "BML_CREDIT" } - val cards = accounts.filter { it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" } + val regularAccounts = accounts.filter { it.profileType != "BML_PREPAID" && it.profileType != "BML_CREDIT" && it.profileType != "BML_DEBIT" } + val cards = accounts.filter { it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT" } val filteredRegular = if (search.isBlank()) regularAccounts else regularAccounts.filter { it.accountBriefName.contains(search, ignoreCase = true) || it.accountNumber.contains(search) diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt index a5a72b9..d60e9e6 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt @@ -21,6 +21,7 @@ import sh.sar.basedbank.api.bml.BmlForeignLimit import sh.sar.basedbank.api.models.BankAccount import sh.sar.basedbank.api.mib.MibCard import sh.sar.basedbank.api.mib.MibFinanceDeal +import sh.sar.basedbank.util.bmlapi.BmlCardParser import kotlin.math.abs import sh.sar.basedbank.databinding.FragmentDashboardBinding import sh.sar.basedbank.databinding.ItemForeignLimitBinding @@ -57,11 +58,17 @@ class DashboardFragment : Fragment() { binding.rvCards.adapter = cardAdapter LinearSnapHelper().attachToRecyclerView(binding.rvCards) - viewModel.mibCards.observe(viewLifecycleOwner) { cards -> - if (cards.isNullOrEmpty()) return@observe - cardAdapter.update(cards) - binding.sectionCards.visibility = View.VISIBLE + val updateCardList = { + val mibItems = (viewModel.mibCards.value ?: emptyList()).map { CardItem.Mib(it) } + val bmlItems = (viewModel.accounts.value ?: emptyList()) + .filter { (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT") && it.statusDesc.equals("Active", ignoreCase = true) } + .map { CardItem.Bml(it) } + val all = mibItems + bmlItems + cardAdapter.update(all) + binding.sectionCards.visibility = if (all.isNotEmpty()) View.VISIBLE else View.GONE } + viewModel.mibCards.observe(viewLifecycleOwner) { updateCardList() } + viewModel.accounts.observe(viewLifecycleOwner) { updateCardList() } val bottomPaddingBase = (16 * resources.displayMetrics.density).toInt() ViewCompat.setOnApplyWindowInsetsListener(binding.buttonBar) { v, insets -> @@ -229,9 +236,9 @@ class DashboardFragment : Fragment() { } private inner class DashboardCardAdapter : RecyclerView.Adapter() { - private var cards: List = emptyList() + private var cards: List = emptyList() - fun update(newCards: List) { + fun update(newCards: List) { cards = newCards notifyDataSetChanged() } @@ -249,15 +256,27 @@ class DashboardFragment : Fragment() { private val ivCardImage: ImageView = view.findViewById(R.id.ivCardImage) private val tvCardOwner: TextView = view.findViewById(R.id.tvCardOwner) private val tvCardNumber: TextView = view.findViewById(R.id.tvCardNumber) + private val tvCardStatus: TextView = view.findViewById(R.id.tvCardStatus) private val btnPayQr: View = view.findViewById(R.id.btnPayQr) private val btnPayNfc: View = view.findViewById(R.id.btnPayNfc) - fun bind(card: MibCard) { - tvCardOwner.text = card.cardHolderName - tvCardNumber.text = PayWithCardFragment.formatMasked(card.maskedCardNumber) - val assetPath = PayWithCardFragment.cardImageAsset(card) - if (assetPath != null) PayWithCardFragment.loadCardImage(ivCardImage, assetPath) - else ivCardImage.setImageDrawable(null) + fun bind(item: CardItem) { + when (item) { + is CardItem.Mib -> { + tvCardOwner.text = item.card.cardHolderName + tvCardNumber.text = PayWithCardFragment.formatMasked(item.card.maskedCardNumber) + val assetPath = PayWithCardFragment.cardImageAsset(item.card) + if (assetPath != null) PayWithCardFragment.loadCardImage(ivCardImage, assetPath) + else ivCardImage.setImageDrawable(null) + PayWithCardFragment.bindCardStatus(tvCardStatus, PayWithCardFragment.mibCardStatusLabel(item.card.cardStatus)) + } + is CardItem.Bml -> { + tvCardOwner.text = item.account.accountBriefName + tvCardNumber.text = PayWithCardFragment.formatMasked(item.account.accountNumber) + PayWithCardFragment.loadCardImage(ivCardImage, BmlCardParser.cardImageAsset(item.account)) + PayWithCardFragment.bindCardStatus(tvCardStatus, null) // only Active BML cards reach dashboard + } + } btnPayQr.setOnClickListener { Toast.makeText(requireContext(), R.string.work_in_progress, Toast.LENGTH_SHORT).show() } diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/HomeViewModel.kt b/app/src/main/java/sh/sar/basedbank/ui/home/HomeViewModel.kt index 68b4da1..c3b0ffc 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/HomeViewModel.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/HomeViewModel.kt @@ -10,6 +10,11 @@ import sh.sar.basedbank.api.models.BankContactCategory import sh.sar.basedbank.api.mib.MibCard import sh.sar.basedbank.api.mib.MibFinanceDeal +sealed class CardItem { + data class Mib(val card: MibCard) : CardItem() + data class Bml(val account: BankAccount) : CardItem() +} + class HomeViewModel : ViewModel() { val accounts = MutableLiveData>(emptyList()) val financing = MutableLiveData>(emptyList()) diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/PayMvQrFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/PayMvQrFragment.kt index 9660436..d1d0363 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/PayMvQrFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/PayMvQrFragment.kt @@ -97,7 +97,7 @@ class PayMvQrFragment : Fragment() { private fun setupDropdown() { viewModel.accounts.observe(viewLifecycleOwner) { accounts -> val eligible = accounts.filter { - it.profileType != "BML_PREPAID" && it.profileType != "BML_CREDIT" && it.profileType != "BML_LOAN" + it.profileType != "BML_PREPAID" && it.profileType != "BML_CREDIT" && it.profileType != "BML_DEBIT" && it.profileType != "BML_LOAN" } val adapter = QrAccountAdapter(requireContext(), eligible) binding.actvAccount.setAdapter(adapter) diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/PayWithCardFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/PayWithCardFragment.kt index 4fd229c..b461299 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/PayWithCardFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/PayWithCardFragment.kt @@ -2,6 +2,7 @@ package sh.sar.basedbank.ui.home import android.content.Context import android.graphics.BitmapFactory +import android.graphics.drawable.GradientDrawable import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -16,9 +17,11 @@ import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import sh.sar.basedbank.R +import sh.sar.basedbank.api.models.BankAccount import sh.sar.basedbank.api.mib.MibCard import sh.sar.basedbank.databinding.FragmentPayWithCardBinding import sh.sar.basedbank.util.CardsCache +import sh.sar.basedbank.util.bmlapi.BmlCardParser class PayWithCardFragment : Fragment() { @@ -46,17 +49,23 @@ class PayWithCardFragment : Fragment() { } binding.swipeRefresh.setOnRefreshListener { - (activity as? HomeActivity)?.triggerRefreshCards() + (activity as? HomeActivity)?.triggerRefresh() } - viewModel.mibCards.observe(viewLifecycleOwner) { cards -> - if (cards == null) return@observe - adapter.update(cards) + val updateCardList = { + val mibItems = (viewModel.mibCards.value ?: emptyList()).map { CardItem.Mib(it) } + val bmlItems = (viewModel.accounts.value ?: emptyList()) + .filter { (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT") && it.statusDesc.equals("Active", ignoreCase = true) } + .map { CardItem.Bml(it) } + val all = mibItems + bmlItems + adapter.update(all) binding.loadingView.visibility = View.GONE binding.swipeRefresh.isRefreshing = false - binding.emptyView.visibility = if (cards.isEmpty()) View.VISIBLE else View.GONE - binding.recyclerView.visibility = if (cards.isEmpty()) View.GONE else View.VISIBLE + binding.emptyView.visibility = if (all.isEmpty()) View.VISIBLE else View.GONE + binding.recyclerView.visibility = if (all.isEmpty()) View.GONE else View.VISIBLE } + viewModel.mibCards.observe(viewLifecycleOwner) { updateCardList() } + viewModel.accounts.observe(viewLifecycleOwner) { updateCardList() } val cached = CardsCache.load(requireContext()) if (cached.isNotEmpty()) { @@ -78,11 +87,11 @@ class PayWithCardFragment : Fragment() { } private inner class CardWalletAdapter( - private var cards: List, + private var cards: List, private val context: Context ) : RecyclerView.Adapter() { - fun update(newCards: List) { + fun update(newCards: List) { cards = newCards notifyDataSetChanged() } @@ -98,16 +107,30 @@ class PayWithCardFragment : Fragment() { private val tvCardOwner: TextView = view.findViewById(R.id.tvCardOwner) private val tvCardNumber: TextView = view.findViewById(R.id.tvCardNumber) private val tvCardType: TextView = view.findViewById(R.id.tvCardType) + private val tvCardStatus: TextView = view.findViewById(R.id.tvCardStatus) private val btnPayQr: View = view.findViewById(R.id.btnPayQr) private val btnPayNfc: View = view.findViewById(R.id.btnPayNfc) - fun bind(card: MibCard) { - tvCardOwner.text = card.cardHolderName - tvCardNumber.text = formatMasked(card.maskedCardNumber) - tvCardType.text = card.cardTypeDesc - val assetPath = cardImageAsset(card) - if (assetPath != null) loadCardImage(ivCardImage, assetPath) - else ivCardImage.setImageDrawable(null) + fun bind(item: CardItem) { + when (item) { + is CardItem.Mib -> { + tvCardOwner.text = item.card.cardHolderName + tvCardNumber.text = formatMasked(item.card.maskedCardNumber) + tvCardType.text = item.card.cardTypeDesc + val assetPath = cardImageAsset(item.card) + if (assetPath != null) loadCardImage(ivCardImage, assetPath) + else ivCardImage.setImageDrawable(null) + bindCardStatus(tvCardStatus, mibCardStatusLabel(item.card.cardStatus)) + } + is CardItem.Bml -> { + tvCardOwner.text = item.account.accountBriefName + tvCardNumber.text = formatMasked(item.account.accountNumber) + tvCardType.text = item.account.accountTypeName + loadCardImage(ivCardImage, BmlCardParser.cardImageAsset(item.account)) + val bmlStatus = item.account.statusDesc.takeUnless { it.equals("Active", ignoreCase = true) } + bindCardStatus(tvCardStatus, bmlStatus) + } + } btnPayQr.setOnClickListener { Toast.makeText(context, R.string.work_in_progress, Toast.LENGTH_SHORT).show() } @@ -127,9 +150,9 @@ class PayWithCardFragment : Fragment() { companion object { fun cardImageAsset(card: MibCard): String? = when (card.cardType) { - "53" -> "cards/mib/visa_black_platinum.jpg" - "57" -> "cards/mib/visa_blue_everyday.jpg" - "70" -> "cards/mib/visa_business.jpg" + "53" -> "cards/mib/visa_black_platinum.png" + "57" -> "cards/mib/visa_blue_everyday.png" + "70" -> "cards/mib/visa_business.png" else -> null } @@ -144,6 +167,23 @@ class PayWithCardFragment : Fragment() { } } + fun mibCardStatusLabel(cardStatus: String): String? = when (cardStatus) { + "CHST0" -> null // Active โ€” no badge + else -> cardStatus + } + + fun bindCardStatus(tv: TextView, statusLabel: String?) { + if (statusLabel == null) { tv.visibility = View.GONE; return } + tv.visibility = View.VISIBLE + tv.text = statusLabel + val dp = tv.context.resources.displayMetrics.density + tv.background = GradientDrawable().apply { + shape = GradientDrawable.RECTANGLE + cornerRadius = 12 * dp + setColor(0xCC212121.toInt()) + } + } + fun formatMasked(masked: String): String { if (masked.length < 4) return masked return "\u2022\u2022\u2022\u2022 ${masked.takeLast(4)}" diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt index 0c2fc00..99413b6 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt @@ -283,6 +283,7 @@ class TransferFragment : Fragment() { val typeLabel = when { account.profileType == "BML_PREPAID" -> "Prepaid Card" account.profileType == "BML_CREDIT" -> "Credit Card" + account.profileType == "BML_DEBIT" -> "Debit Card" account.accountTypeName.isNotBlank() -> account.accountTypeName else -> account.profileType } @@ -632,7 +633,7 @@ class TransferFragment : Fragment() { val isSrcBml = src.bank == "BML" val isBmlBusiness = isSrcBml && isBusinessProfile(src) // to test on personal accounts: use `isSrcBml` - val isSrcCard = src.profileType == "BML_PREPAID" || src.profileType == "BML_CREDIT" + val isSrcCard = src.profileType == "BML_PREPAID" || src.profileType == "BML_CREDIT" || src.profileType == "BML_DEBIT" val isDestMib = AccountInputParser.detect(resolvedAccountNumber) == AccountInputParser.InputType.MIB_ACCOUNT val currency = src.currencyName.ifBlank { "MVR" } val allAccounts = viewModel.accounts.value ?: emptyList() @@ -860,7 +861,7 @@ class TransferFragment : Fragment() { } // Determine type + credit account - val isDestMyCard = allAccounts.any { (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT") && it.accountNumber == destAccount } + val isDestMyCard = allAccounts.any { (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT") && it.accountNumber == destAccount } val (transferType, creditAccount, bank) = when { isSrcCard -> { // CAD: card โ†’ own BML account @@ -869,7 +870,7 @@ class TransferFragment : Fragment() { } isDestMyCard -> { // CPA: BML CASA โ†’ own card top-up - val card = allAccounts.first { (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT") && it.accountNumber == destAccount } + val card = allAccounts.first { (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT") && it.accountNumber == destAccount } Triple("CPA", card.internalId.ifBlank { destAccount }, null as String?) } isDestMib && currency == "MVR" -> Triple("DOT", destAccount, "MIB") @@ -950,7 +951,7 @@ class TransferFragment : Fragment() { return } val isDestMyCard = allAccounts.any { - (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT") && it.accountNumber == destAccount + (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT") && it.accountNumber == destAccount } val (transferType, creditAccount, bank) = when { isSrcCard -> { @@ -959,7 +960,7 @@ class TransferFragment : Fragment() { } isDestMyCard -> { val card = allAccounts.first { - (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT") && it.accountNumber == destAccount + (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT") && it.accountNumber == destAccount } Triple("CPA", card.internalId.ifBlank { destAccount }, null as String?) } @@ -1337,8 +1338,8 @@ class TransferFragment : Fragment() { ) : BaseAdapter(), Filterable { private val items: List = buildList { - val regular = accounts.filter { it.profileType != "BML_PREPAID" && it.profileType != "BML_CREDIT" && it.profileType != "BML_LOAN" } - val cards = accounts.filter { it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" } + val regular = accounts.filter { it.profileType != "BML_PREPAID" && it.profileType != "BML_CREDIT" && it.profileType != "BML_DEBIT" && it.profileType != "BML_LOAN" } + val cards = accounts.filter { it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT" } addAll(regular) if (cards.isNotEmpty()) { add(getString(R.string.cards)) @@ -1347,7 +1348,7 @@ class TransferFragment : Fragment() { } fun getAccount(position: Int): BankAccount? = (items.getOrNull(position) as? BankAccount) - ?.takeUnless { (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT") && !it.statusDesc.equals("Active", ignoreCase = true) } + ?.takeUnless { (it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT") && !it.statusDesc.equals("Active", ignoreCase = true) } override fun getCount() = items.size override fun getItem(position: Int) = items[position] @@ -1378,7 +1379,7 @@ class TransferFragment : Fragment() { ItemAccountDropdownBinding.inflate(LayoutInflater.from(context), parent, false) .also { it.root.tag = it } } - val inactive = (acc.profileType == "BML_PREPAID" || acc.profileType == "BML_CREDIT") && !acc.statusDesc.equals("Active", ignoreCase = true) + val inactive = (acc.profileType == "BML_PREPAID" || acc.profileType == "BML_CREDIT" || acc.profileType == "BML_DEBIT") && !acc.statusDesc.equals("Active", ignoreCase = true) val isBmlAccount = acc.bank == "BML" val ownerPrefix = if (isBmlAccount && acc.profileName.isNotBlank()) "${acc.profileName} ยท " else "" b.tvDropdownAccountName.text = "$ownerPrefix${acc.accountBriefName}" diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/TransferHistoryFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/TransferHistoryFragment.kt index 31360fa..47b7158 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/TransferHistoryFragment.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/TransferHistoryFragment.kt @@ -64,7 +64,7 @@ class TransferHistoryFragment : Fragment() { ) { fun hasMore(): Boolean = when { account.bank == "FAHIPAY" -> fahipayTotal < 0 || fahipayNextStart < fahipayTotal - account.profileType == "BML_PREPAID" || account.profileType == "BML_CREDIT" -> cardMonthOffset < 2 + account.profileType == "BML_PREPAID" || account.profileType == "BML_CREDIT" || account.profileType == "BML_DEBIT" -> cardMonthOffset < 2 account.bank == "BML" -> bmlTotalPages < 0 || bmlNextPage <= bmlTotalPages else -> mibTotalCount < 0 || mibNextStart <= mibTotalCount } @@ -187,7 +187,7 @@ class TransferHistoryFragment : Fragment() { async { try { when { - state.account.profileType == "BML_PREPAID" || state.account.profileType == "BML_CREDIT" -> { + state.account.profileType == "BML_PREPAID" || state.account.profileType == "BML_CREDIT" || state.account.profileType == "BML_DEBIT" -> { val session = app.bmlSessionFor(state.account) ?: return@async emptyList() val cal = Calendar.getInstance() cal.add(Calendar.MONTH, -state.cardMonthOffset) diff --git a/app/src/main/java/sh/sar/basedbank/util/HistoryFetcher.kt b/app/src/main/java/sh/sar/basedbank/util/HistoryFetcher.kt index e8676fa..9dce590 100644 --- a/app/src/main/java/sh/sar/basedbank/util/HistoryFetcher.kt +++ b/app/src/main/java/sh/sar/basedbank/util/HistoryFetcher.kt @@ -21,7 +21,7 @@ import java.util.Locale class HistoryFetcher(private val account: BankAccount) { private val isMib get() = account.bank == "MIB" - private val isBmlCard get() = account.profileType == "BML_PREPAID" || account.profileType == "BML_CREDIT" + private val isBmlCard get() = account.profileType == "BML_PREPAID" || account.profileType == "BML_CREDIT" || account.profileType == "BML_DEBIT" private val isBmlLoan get() = account.profileType == "BML_LOAN" private val isFahipay get() = account.bank == "FAHIPAY" diff --git a/app/src/main/java/sh/sar/basedbank/util/bmlapi/BmlCardParser.kt b/app/src/main/java/sh/sar/basedbank/util/bmlapi/BmlCardParser.kt new file mode 100644 index 0000000..0966755 --- /dev/null +++ b/app/src/main/java/sh/sar/basedbank/util/bmlapi/BmlCardParser.kt @@ -0,0 +1,45 @@ +package sh.sar.basedbank.util.bmlapi + +import sh.sar.basedbank.api.models.BankAccount + +object BmlCardParser { + + /** + * Returns the asset path for the card image. + * The product code is stored in [BankAccount.cifType] for BML Card accounts. + */ + fun cardImageAsset(account: BankAccount): String = + productCodeToAsset(account.cifType) + + fun productCodeToAsset(productCode: String): String = when (productCode) { + "C8201" -> "cards/bml/master_prepaid.png" + "C8205" -> "cards/bml/master_prepaid_travel.png" + "C3007" -> "cards/bml/amex_debit_green.png" + "C1007" -> "cards/bml/visa_debit.png" + "C1003" -> "cards/bml/visa_gold.png" + "C8022" -> "cards/bml/master_gold.png" + "C1020" -> "cards/bml/visa_debit_platinum.png" + "C8902" -> "cards/bml/master_islamic.png" + "C8101" -> "cards/bml/master_masveriyaa.png" + // "?????" -> "cards/bml/amex_credit_gold.png" + // "?????" -> "cards/bml/amex_credit_green.png" + // "?????" -> "cards/bml/amex_debit_gold.png" + // "?????" -> "cards/bml/amex_platinum.png" + // "?????" -> "cards/bml/master.png" + // "?????" -> "cards/bml/master_business_debit.png" + // "?????" -> "cards/bml/master_odiveriyaa.png" + // "?????" -> "cards/bml/master_passport.png" + // "?????" -> "cards/bml/master_platinum.png" + // "?????" -> "cards/bml/master_prepaid_business.png" + // "?????" -> "cards/bml/master_world.png" + // "?????" -> "cards/bml/visa_corporate.png" + // "?????" -> "cards/bml/visa_credit.png" + // "?????" -> "cards/bml/visa_debit_generic.png" + // "?????" -> "cards/bml/visa_debit_islamic.png" + // "?????" -> "cards/bml/visa_infinite.png" + // "?????" -> "cards/bml/visa_platinum.png" + // "?????" -> "cards/bml/visa_student_black.png" + // "?????" -> "cards/bml/visa_student_blue.png" + else -> "cards/bml/defaultcard.png" + } +} diff --git a/app/src/main/java/sh/sar/basedbank/util/bmlapi/BmlDashboardParser.kt b/app/src/main/java/sh/sar/basedbank/util/bmlapi/BmlDashboardParser.kt index fbf0f4f..0ce1488 100644 --- a/app/src/main/java/sh/sar/basedbank/util/bmlapi/BmlDashboardParser.kt +++ b/app/src/main/java/sh/sar/basedbank/util/bmlapi/BmlDashboardParser.kt @@ -12,7 +12,7 @@ object BmlDashboardParser { */ fun displayData(account: BankAccount): AccountListDisplay? { if (account.profileType == "BML_LOAN") return null // Loans shown on financing page only - val isCard = account.profileType == "BML_PREPAID" || account.profileType == "BML_CREDIT" + val isCard = account.profileType == "BML_PREPAID" || account.profileType == "BML_CREDIT" || account.profileType == "BML_DEBIT" return if (isCard) { val isActive = account.statusDesc.equals("Active", ignoreCase = true) AccountListDisplay( diff --git a/app/src/main/res/layout/item_card_dashboard.xml b/app/src/main/res/layout/item_card_dashboard.xml index c194e27..d705736 100644 --- a/app/src/main/res/layout/item_card_dashboard.xml +++ b/app/src/main/res/layout/item_card_dashboard.xml @@ -31,6 +31,21 @@ android:layout_gravity="bottom" android:background="@drawable/bg_card_overlay_gradient"/> + + + + + +