forked from shihaam/thijooree
show bank/profile image in accounts and drop down
This commit is contained in:
@@ -3,11 +3,13 @@ package sh.sar.basedbank.ui.home
|
|||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import sh.sar.basedbank.R
|
||||||
import sh.sar.basedbank.api.models.BankAccount
|
import sh.sar.basedbank.api.models.BankAccount
|
||||||
import sh.sar.basedbank.databinding.ItemAccountBinding
|
import sh.sar.basedbank.databinding.ItemAccountBinding
|
||||||
import sh.sar.basedbank.databinding.ItemCardBinding
|
import sh.sar.basedbank.databinding.ItemCardBinding
|
||||||
@@ -17,7 +19,9 @@ import sh.sar.basedbank.util.AccountListParser
|
|||||||
|
|
||||||
class AccountsAdapter(
|
class AccountsAdapter(
|
||||||
accounts: List<BankAccount>,
|
accounts: List<BankAccount>,
|
||||||
private val onAccountClick: (BankAccount) -> Unit = {}
|
private val onAccountClick: (BankAccount) -> Unit = {},
|
||||||
|
/** Optional loader for MIB per-profile images: (hash, onLoaded) */
|
||||||
|
private val profileImageLoader: ((String, (Bitmap) -> Unit) -> Unit)? = null
|
||||||
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
var onTransferClick: ((BankAccount) -> Unit)? = null
|
var onTransferClick: ((BankAccount) -> Unit)? = null
|
||||||
@@ -112,6 +116,8 @@ class AccountsAdapter(
|
|||||||
|
|
||||||
private inner class AccountViewHolder(private val binding: ItemAccountBinding) :
|
private inner class AccountViewHolder(private val binding: ItemAccountBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
|
private var boundHash: String? = null
|
||||||
|
|
||||||
fun bind(account: BankAccount, display: AccountListDisplay) {
|
fun bind(account: BankAccount, display: AccountListDisplay) {
|
||||||
binding.tvAccountName.text = display.name
|
binding.tvAccountName.text = display.name
|
||||||
binding.tvAccountNumber.text = display.number
|
binding.tvAccountNumber.text = display.number
|
||||||
@@ -123,6 +129,23 @@ class AccountsAdapter(
|
|||||||
copyToClipboard(it.context, display.number)
|
copyToClipboard(it.context, display.number)
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val staticLogo = when (account.bank) {
|
||||||
|
"BML" -> R.drawable.bml_logo_vector
|
||||||
|
"FAHIPAY" -> R.drawable.fahipay_logo
|
||||||
|
"MIB" -> R.drawable.mib_logo
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
if (staticLogo != null) binding.ivBankLogo.setImageResource(staticLogo)
|
||||||
|
else binding.ivBankLogo.setImageDrawable(null)
|
||||||
|
|
||||||
|
val hash = account.profileImageHash
|
||||||
|
boundHash = hash
|
||||||
|
if (account.bank == "MIB" && hash != null && profileImageLoader != null) {
|
||||||
|
profileImageLoader.invoke(hash) { bitmap ->
|
||||||
|
if (boundHash == hash) binding.ivBankLogo.setImageBitmap(bitmap)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package sh.sar.basedbank.ui.home
|
package sh.sar.basedbank.ui.home
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Base64
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
@@ -8,7 +11,12 @@ import androidx.core.view.ViewCompat
|
|||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import sh.sar.basedbank.BasedBankApp
|
||||||
import sh.sar.basedbank.R
|
import sh.sar.basedbank.R
|
||||||
import sh.sar.basedbank.databinding.FragmentAccountsBinding
|
import sh.sar.basedbank.databinding.FragmentAccountsBinding
|
||||||
|
|
||||||
@@ -18,6 +26,7 @@ class AccountsFragment : Fragment() {
|
|||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
private val viewModel: HomeViewModel by activityViewModels()
|
private val viewModel: HomeViewModel by activityViewModels()
|
||||||
private lateinit var adapter: AccountsAdapter
|
private lateinit var adapter: AccountsAdapter
|
||||||
|
private val profileImageCache = mutableMapOf<String, Bitmap>()
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||||
_binding = FragmentAccountsBinding.inflate(inflater, container, false)
|
_binding = FragmentAccountsBinding.inflate(inflater, container, false)
|
||||||
@@ -25,9 +34,30 @@ class AccountsFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
adapter = AccountsAdapter(emptyList()) { account ->
|
val app = requireActivity().application as BasedBankApp
|
||||||
(activity as? HomeActivity)?.showWithBackStack(AccountHistoryFragment.newInstance(account))
|
adapter = AccountsAdapter(
|
||||||
}
|
accounts = emptyList(),
|
||||||
|
onAccountClick = { account ->
|
||||||
|
(activity as? HomeActivity)?.showWithBackStack(AccountHistoryFragment.newInstance(account))
|
||||||
|
},
|
||||||
|
profileImageLoader = { hash, onLoaded ->
|
||||||
|
profileImageCache[hash]?.let { onLoaded(it); return@AccountsAdapter }
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
val bitmap = withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val session = app.anyMibSession() ?: return@withContext null
|
||||||
|
val b64 = app.anyMibFlow()?.fetchProfileImage(session, hash) ?: return@withContext null
|
||||||
|
val bytes = Base64.decode(b64, Base64.DEFAULT)
|
||||||
|
BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
|
||||||
|
} catch (_: Exception) { null }
|
||||||
|
}
|
||||||
|
if (bitmap != null) {
|
||||||
|
profileImageCache[hash] = bitmap
|
||||||
|
onLoaded(bitmap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
adapter.onTransferClick = { account ->
|
adapter.onTransferClick = { account ->
|
||||||
(activity as? HomeActivity)?.navigateTo(R.id.nav_transfer, TransferFragment.newInstanceFrom(account))
|
(activity as? HomeActivity)?.navigateTo(R.id.nav_transfer, TransferFragment.newInstanceFrom(account))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ class TransferFragment : Fragment() {
|
|||||||
// BML QR merchant payment mode (set when navigated from a card QR scan)
|
// BML QR merchant payment mode (set when navigated from a card QR scan)
|
||||||
private var bmlQrInfo: BmlQrPayInfo? = null
|
private var bmlQrInfo: BmlQrPayInfo? = null
|
||||||
private var bmlGatewayQr = false // true for pay.bml.com.mv QRs (requires pre-initiate step)
|
private var bmlGatewayQr = false // true for pay.bml.com.mv QRs (requires pre-initiate step)
|
||||||
|
private val dropdownProfileImageCache = mutableMapOf<String, Bitmap>()
|
||||||
|
|
||||||
// BML business profile OTP flow state
|
// BML business profile OTP flow state
|
||||||
private enum class BmlOtpState { NONE, SELECTING_CHANNEL, AWAITING_OTP }
|
private enum class BmlOtpState { NONE, SELECTING_CHANNEL, AWAITING_OTP }
|
||||||
@@ -1629,11 +1630,47 @@ class TransferFragment : Fragment() {
|
|||||||
b.tvDropdownBalance.text = if (hide && balance.isNotBlank()) maskAmount(balance) else balance
|
b.tvDropdownBalance.text = if (hide && balance.isNotBlank()) maskAmount(balance) else balance
|
||||||
b.root.alpha = if (inactive) 0.4f else 1f
|
b.root.alpha = if (inactive) 0.4f else 1f
|
||||||
val networkIcon = BmlCardParser.cardNetworkIcon(acc)
|
val networkIcon = BmlCardParser.cardNetworkIcon(acc)
|
||||||
if (networkIcon != null) {
|
when {
|
||||||
b.ivDropdownCardLogo.setImageResource(networkIcon)
|
networkIcon != null -> {
|
||||||
b.ivDropdownCardLogo.visibility = View.VISIBLE
|
b.ivDropdownCardLogo.setImageResource(networkIcon)
|
||||||
} else {
|
b.ivDropdownCardLogo.visibility = View.VISIBLE
|
||||||
b.ivDropdownCardLogo.visibility = View.GONE
|
}
|
||||||
|
acc.bank == "BML" -> {
|
||||||
|
b.ivDropdownCardLogo.setImageResource(R.drawable.bml_logo_vector)
|
||||||
|
b.ivDropdownCardLogo.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
acc.bank == "FAHIPAY" -> {
|
||||||
|
b.ivDropdownCardLogo.setImageResource(R.drawable.fahipay_logo)
|
||||||
|
b.ivDropdownCardLogo.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
acc.bank == "MIB" -> {
|
||||||
|
val hash = acc.profileImageHash
|
||||||
|
val cached = hash?.let { dropdownProfileImageCache[it] }
|
||||||
|
if (cached != null) {
|
||||||
|
b.ivDropdownCardLogo.setImageBitmap(cached)
|
||||||
|
} else {
|
||||||
|
b.ivDropdownCardLogo.setImageResource(R.drawable.mib_logo)
|
||||||
|
if (hash != null) {
|
||||||
|
val app = requireActivity().application as BasedBankApp
|
||||||
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
|
val bitmap = withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
val sess = app.anyMibSession() ?: return@withContext null
|
||||||
|
val b64 = app.anyMibFlow()?.fetchProfileImage(sess, hash) ?: return@withContext null
|
||||||
|
val bytes = android.util.Base64.decode(b64, android.util.Base64.DEFAULT)
|
||||||
|
android.graphics.BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
|
||||||
|
} catch (_: Exception) { null }
|
||||||
|
}
|
||||||
|
if (bitmap != null) {
|
||||||
|
dropdownProfileImageCache[hash] = bitmap
|
||||||
|
accountDropdownAdapter?.notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
b.ivDropdownCardLogo.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
else -> b.ivDropdownCardLogo.visibility = View.GONE
|
||||||
}
|
}
|
||||||
b.root
|
b.root
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,16 @@
|
|||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:foreground="?attr/selectableItemBackground">
|
android:foreground="?attr/selectableItemBackground">
|
||||||
|
|
||||||
|
<!-- Bank / profile logo -->
|
||||||
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
|
android:id="@+id/ivBankLogo"
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:scaleType="fitCenter"
|
||||||
|
android:layout_marginEnd="12dp"
|
||||||
|
app:shapeAppearanceOverlay="@style/ShapeAppearance.Circle"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" />
|
||||||
|
|
||||||
<!-- Left: name / number -->
|
<!-- Left: name / number -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
|
|||||||
@@ -8,13 +8,15 @@
|
|||||||
android:paddingHorizontal="16dp"
|
android:paddingHorizontal="16dp"
|
||||||
android:paddingVertical="12dp">
|
android:paddingVertical="12dp">
|
||||||
|
|
||||||
<ImageView
|
<com.google.android.material.imageview.ShapeableImageView
|
||||||
android:id="@+id/ivDropdownCardLogo"
|
android:id="@+id/ivDropdownCardLogo"
|
||||||
android:layout_width="36dp"
|
android:layout_width="36dp"
|
||||||
android:layout_height="24dp"
|
android:layout_height="36dp"
|
||||||
android:scaleType="fitCenter"
|
android:scaleType="fitCenter"
|
||||||
android:layout_marginEnd="10dp"
|
android:layout_marginEnd="10dp"
|
||||||
android:visibility="gone" />
|
android:visibility="gone"
|
||||||
|
app:shapeAppearanceOverlay="@style/ShapeAppearance.Circle"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
Reference in New Issue
Block a user