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 d2dc076..f2278ad 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 @@ -1,139 +1,3 @@ package sh.sar.basedbank.ui.home -import android.content.Context -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ImageView -import android.widget.TextView -import android.widget.Toast -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.fragment.app.Fragment -import androidx.fragment.app.activityViewModels -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import sh.sar.basedbank.R -import sh.sar.basedbank.databinding.FragmentCardSettingsBinding -import sh.sar.basedbank.util.bmlapi.BmlCardParser - -class CardSettingsFragment : Fragment() { - - private var _binding: FragmentCardSettingsBinding? = null - private val binding get() = _binding!! - private val viewModel: HomeViewModel by activityViewModels() - - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - _binding = FragmentCardSettingsBinding.inflate(inflater, container, false) - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - val adapter = CardSettingsAdapter(emptyList(), requireContext()) - binding.recyclerView.layoutManager = LinearLayoutManager(requireContext()) - binding.recyclerView.adapter = adapter - - val bottomPaddingBase = (16 * resources.displayMetrics.density).toInt() - ViewCompat.setOnApplyWindowInsetsListener(binding.recyclerView) { v, insets -> - val isBottomNav = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE).getBoolean("bottom_nav", false) - val navBar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) - val extraBottom = if (isBottomNav) 0 else navBar.bottom - v.setPadding(v.paddingLeft, v.paddingTop, v.paddingRight, bottomPaddingBase + extraBottom) - insets - } - - binding.swipeRefresh.setOnRefreshListener { - (activity as? HomeActivity)?.triggerRefresh() - } - - 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 (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 - (activity as? HomeActivity)?.triggerRefreshCards() - } - } - - override fun onResume() { - super.onResume() - requireActivity().title = getString(R.string.nav_card_settings) - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null - } - - private inner class CardSettingsAdapter( - private var cards: List, - private val context: Context - ) : RecyclerView.Adapter() { - - fun update(newCards: List) { - cards = newCards - notifyDataSetChanged() - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH = - VH(LayoutInflater.from(context).inflate(R.layout.item_card_settings_entry, parent, false)) - - override fun onBindViewHolder(holder: VH, position: Int) = holder.bind(cards[position]) - override fun getItemCount() = cards.size - - inner class VH(view: View) : RecyclerView.ViewHolder(view) { - 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 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(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() - } - btnChangePin.setOnClickListener(wip) - btnFreeze.setOnClickListener(wip) - btnBlock.setOnClickListener(wip) - } - } - } -} +// Merged into CardsFragment 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 8fbef5f..e3cc631 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 @@ -287,17 +287,17 @@ class DashboardFragment : Fragment() { 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) + tvCardNumber.text = CardsFragment.formatMasked(item.card.maskedCardNumber) + val assetPath = CardsFragment.cardImageAsset(item.card) + if (assetPath != null) CardsFragment.loadCardImage(ivCardImage, assetPath) else ivCardImage.setImageDrawable(null) - PayWithCardFragment.bindCardStatus(tvCardStatus, PayWithCardFragment.mibCardStatusLabel(item.card.cardStatus)) + CardsFragment.bindCardStatus(tvCardStatus, CardsFragment.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 + tvCardNumber.text = CardsFragment.formatMasked(item.account.accountNumber) + CardsFragment.loadCardImage(ivCardImage, BmlCardParser.cardImageAsset(item.account)) + CardsFragment.bindCardStatus(tvCardStatus, null) // only Active BML cards reach dashboard } } val isMib = item is CardItem.Mib diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/HomeActivity.kt b/app/src/main/java/sh/sar/basedbank/ui/home/HomeActivity.kt index eaef1be..96219fe 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/HomeActivity.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/HomeActivity.kt @@ -159,8 +159,7 @@ class HomeActivity : AppCompatActivity() { R.id.nav_finances -> FinancingFragment() R.id.nav_otp -> OtpFragment() R.id.nav_settings -> SettingsFragment() - R.id.nav_pay_with_card -> PayWithCardFragment() - R.id.nav_card_settings -> CardSettingsFragment() + R.id.nav_pay_with_card -> CardsFragment() else -> null } if (frag != null) show(frag) @@ -379,8 +378,7 @@ fun applyNavLabelVisibility() { R.id.nav_finances -> FinancingFragment() R.id.nav_otp -> OtpFragment() R.id.nav_settings -> SettingsFragment() - R.id.nav_pay_with_card -> PayWithCardFragment() - R.id.nav_card_settings -> CardSettingsFragment() + R.id.nav_pay_with_card -> CardsFragment() else -> { Toast.makeText(this, R.string.work_in_progress, Toast.LENGTH_SHORT).show(); return } } show(dest) diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/NavCustomization.kt b/app/src/main/java/sh/sar/basedbank/ui/home/NavCustomization.kt index 91a3426..e275df1 100644 --- a/app/src/main/java/sh/sar/basedbank/ui/home/NavCustomization.kt +++ b/app/src/main/java/sh/sar/basedbank/ui/home/NavCustomization.kt @@ -25,7 +25,6 @@ object NavCustomization { NavItemDef(R.id.nav_transfer_history, "nav_transfer_history", R.drawable.ic_nav_transfer_history, R.string.nav_transfer_history, R.string.nav_desc_transfer_history), NavItemDef(R.id.nav_finances, "nav_finances", R.drawable.ic_nav_finances, R.string.nav_finances, R.string.nav_desc_finances), NavItemDef(R.id.nav_pay_with_card, "nav_pay_with_card", R.drawable.ic_nav_card, R.string.nav_pay_with_card, R.string.nav_desc_pay_with_card), - NavItemDef(R.id.nav_card_settings, "nav_card_settings", R.drawable.ic_nav_card, R.string.nav_card_settings, R.string.nav_desc_card_settings), NavItemDef(R.id.nav_otp, "nav_otp", R.drawable.ic_nav_otp, R.string.nav_otp, R.string.nav_desc_otp), NavItemDef(R.id.nav_settings, "nav_settings", R.drawable.ic_nav_settings, R.string.nav_settings, R.string.nav_desc_settings), ) 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 0da63f7..f361cd0 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 @@ -3,13 +3,14 @@ package sh.sar.basedbank.ui.home import android.app.Activity import android.content.Context import android.content.Intent -import android.graphics.BitmapFactory +import android.graphics.Color import android.graphics.drawable.GradientDrawable import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.ImageView +import android.widget.LinearLayout import android.widget.TextView import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts @@ -18,20 +19,25 @@ import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.PagerSnapHelper import androidx.recyclerview.widget.RecyclerView +import com.google.android.material.color.MaterialColors 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.databinding.FragmentCardsBinding import sh.sar.basedbank.util.CardsCache import sh.sar.basedbank.util.bmlapi.BmlCardParser +import kotlin.math.abs -class PayWithCardFragment : Fragment() { +class CardsFragment : Fragment() { - private var _binding: FragmentPayWithCardBinding? = null + private var _binding: FragmentCardsBinding? = null private val binding get() = _binding!! private val viewModel: HomeViewModel by activityViewModels() + private var cards: List = emptyList() + private var currentCardPosition: Int = 0 + private var cardWidth: Int = 0 private var pendingQrAccountNumber: String? = null private val qrLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> @@ -49,39 +55,67 @@ class PayWithCardFragment : Fragment() { } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { - _binding = FragmentPayWithCardBinding.inflate(inflater, container, false) + _binding = FragmentCardsBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - val adapter = CardWalletAdapter(emptyList(), requireContext()) - binding.recyclerView.layoutManager = LinearLayoutManager(requireContext()) - binding.recyclerView.adapter = adapter + val screenW = resources.displayMetrics.widthPixels + val peekPx = screenW / 8 + cardWidth = screenW - 2 * peekPx - val bottomPaddingBase = (16 * resources.displayMetrics.density).toInt() - ViewCompat.setOnApplyWindowInsetsListener(binding.recyclerView) { v, insets -> - val isBottomNav = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE).getBoolean("bottom_nav", false) + val stackAdapter = CardStackAdapter(cardWidth) + binding.rvCards.layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false) + binding.rvCards.adapter = stackAdapter + binding.rvCards.setPadding(peekPx, 0, peekPx, 0) + binding.rvCards.clipToPadding = false + + val snapHelper = PagerSnapHelper() + snapHelper.attachToRecyclerView(binding.rvCards) + + binding.rvCards.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + applyCardScales() + } + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + val lm = recyclerView.layoutManager ?: return + val snapView = snapHelper.findSnapView(lm) ?: return + val position = lm.getPosition(snapView) + if (position >= 0) { + currentCardPosition = position + buildDots(cards.size, position) + updateCardInfo(position) + } + applyCardScales() + } + } + }) + +ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets -> + val isBottomNav = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE) + .getBoolean("bottom_nav", false) val navBar = insets.getInsets(WindowInsetsCompat.Type.systemBars()) val extraBottom = if (isBottomNav) 0 else navBar.bottom - v.setPadding(v.paddingLeft, v.paddingTop, v.paddingRight, bottomPaddingBase + extraBottom) + v.setPadding(0, 0, 0, (16 * resources.displayMetrics.density).toInt() + extraBottom) insets } - binding.swipeRefresh.setOnRefreshListener { - (activity as? HomeActivity)?.triggerRefresh() - } - 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) } + .filter { it.profileType == "BML_PREPAID" || it.profileType == "BML_CREDIT" || it.profileType == "BML_DEBIT" } .map { CardItem.Bml(it) } - val all = mibItems + bmlItems - adapter.update(all) + cards = mibItems + bmlItems + stackAdapter.update(cards) binding.loadingView.visibility = View.GONE - binding.swipeRefresh.isRefreshing = false - binding.emptyView.visibility = if (all.isEmpty()) View.VISIBLE else View.GONE - binding.recyclerView.visibility = if (all.isEmpty()) View.GONE else View.VISIBLE + val empty = cards.isEmpty() + binding.emptyView.visibility = if (empty) View.VISIBLE else View.GONE + binding.contentLayout.visibility = if (empty) View.GONE else View.VISIBLE + if (!empty) { + buildDots(cards.size, currentCardPosition) + updateCardInfo(currentCardPosition) + } } viewModel.mibCards.observe(viewLifecycleOwner) { updateCardList() } viewModel.accounts.observe(viewLifecycleOwner) { updateCardList() } @@ -93,6 +127,84 @@ class PayWithCardFragment : Fragment() { binding.loadingView.visibility = View.VISIBLE } (activity as? HomeActivity)?.triggerRefreshCards() + + binding.btnScanToPay.setOnClickListener { + val item = cards.getOrNull(currentCardPosition) ?: return@setOnClickListener + if (item is CardItem.Mib) { + Toast.makeText(requireContext(), R.string.mib_qr_nfc_not_supported, Toast.LENGTH_SHORT).show() + } else { + pendingQrAccountNumber = (item as CardItem.Bml).account.accountNumber + qrLauncher.launch(Intent(requireContext(), QrScannerActivity::class.java)) + } + } + + val nfcAvailable = android.nfc.NfcAdapter.getDefaultAdapter(requireContext()) != null + binding.btnTapToPay.isEnabled = nfcAvailable + binding.btnTapToPay.setOnClickListener { + val item = cards.getOrNull(currentCardPosition) ?: return@setOnClickListener + val msg = if (item is CardItem.Mib) R.string.mib_qr_nfc_not_supported else R.string.work_in_progress + Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show() + } + + val wip = View.OnClickListener { + Toast.makeText(requireContext(), R.string.work_in_progress, Toast.LENGTH_SHORT).show() + } + binding.btnChangePin.setOnClickListener(wip) + binding.btnFreeze.setOnClickListener(wip) + binding.btnBlock.setOnClickListener(wip) + } + + private fun applyCardScales() { + val rv = binding.rvCards + val rvCenter = rv.paddingStart + (rv.width - rv.paddingStart - rv.paddingEnd) / 2f + val lm = rv.layoutManager as? LinearLayoutManager ?: return + val first = lm.findFirstVisibleItemPosition() + val last = lm.findLastVisibleItemPosition() + if (first < 0) return + for (i in first..last) { + val child = lm.findViewByPosition(i) ?: continue + val childCenter = (child.left + child.right) / 2f + val fraction = (abs(childCenter - rvCenter) / cardWidth.toFloat()).coerceIn(0f, 1f) + val scale = 1f - 0.18f * fraction + child.scaleX = scale + child.scaleY = scale + child.alpha = 1f - 0.4f * fraction + } + } + + private fun buildDots(count: Int, selected: Int) { + binding.pageIndicator.removeAllViews() + if (count <= 1) { + binding.pageIndicator.visibility = View.GONE + return + } + binding.pageIndicator.visibility = View.VISIBLE + val dp = resources.displayMetrics.density + val activeColor = MaterialColors.getColor( + requireContext(), com.google.android.material.R.attr.colorPrimary, Color.GRAY) + val inactiveColor = MaterialColors.getColor( + requireContext(), com.google.android.material.R.attr.colorOutlineVariant, Color.LTGRAY) + val size = (8 * dp).toInt() + val margin = (4 * dp).toInt() + repeat(count) { i -> + val dot = View(requireContext()) + dot.layoutParams = LinearLayout.LayoutParams(size, size).apply { + setMargins(margin, 0, margin, 0) + } + dot.background = GradientDrawable().apply { + shape = GradientDrawable.OVAL + setColor(if (i == selected) activeColor else inactiveColor) + } + binding.pageIndicator.addView(dot) + } + } + + private fun updateCardInfo(position: Int) { + val item = cards.getOrNull(position) ?: return + binding.tvSelectedCardType.text = when (item) { + is CardItem.Mib -> item.card.cardTypeDesc + is CardItem.Bml -> item.account.accountTypeName + } } override fun onResume() { @@ -105,67 +217,59 @@ class PayWithCardFragment : Fragment() { _binding = null } - private inner class CardWalletAdapter( - private var cards: List, - private val context: Context - ) : RecyclerView.Adapter() { + private inner class CardStackAdapter(private val cardWidth: Int) : RecyclerView.Adapter() { + private var items: List = emptyList() - fun update(newCards: List) { - cards = newCards + fun update(newItems: List) { + items = newItems notifyDataSetChanged() } - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH = - VH(LayoutInflater.from(context).inflate(R.layout.item_card_wallet, parent, false)) + override fun getItemCount() = items.size - override fun onBindViewHolder(holder: VH, position: Int) = holder.bind(cards[position]) - override fun getItemCount() = cards.size + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH = + VH(LayoutInflater.from(parent.context).inflate(R.layout.item_card_stack, parent, false)) + + override fun onBindViewHolder(holder: VH, position: Int) { + holder.bind(items[position]) + // Pre-scale based on data position so initial render and off-screen cards are correct + val fraction = abs(position - currentCardPosition).toFloat().coerceIn(0f, 1f) + val scale = 1f - 0.18f * fraction + holder.itemView.scaleX = scale + holder.itemView.scaleY = scale + holder.itemView.alpha = 1f - 0.4f * fraction + } inner class VH(view: View) : RecyclerView.ViewHolder(view) { 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 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) + + init { + itemView.layoutParams = RecyclerView.LayoutParams(cardWidth, RecyclerView.LayoutParams.WRAP_CONTENT) + } 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)) + itemView.alpha = 1f } 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) + val isActive = item.account.statusDesc.equals("Active", ignoreCase = true) + bindCardStatus(tvCardStatus, item.account.statusDesc.takeUnless { isActive }) + itemView.alpha = if (isActive) 1f else 0.45f } } - val isMib = item is CardItem.Mib - btnPayQr.setOnClickListener { - if (isMib) { - Toast.makeText(context, R.string.mib_qr_nfc_not_supported, Toast.LENGTH_SHORT).show() - } else { - pendingQrAccountNumber = (item as CardItem.Bml).account.accountNumber - qrLauncher.launch(Intent(requireContext(), QrScannerActivity::class.java)) - } - } - val nfcAdapter = android.nfc.NfcAdapter.getDefaultAdapter(context) - val nfcSupported = nfcAdapter != null - btnPayNfc.isEnabled = nfcSupported - btnPayNfc.setOnClickListener { - val msg = if (isMib) R.string.mib_qr_nfc_not_supported else R.string.work_in_progress - Toast.makeText(context, msg, Toast.LENGTH_SHORT).show() - } } } } @@ -181,7 +285,7 @@ class PayWithCardFragment : Fragment() { fun loadCardImage(imageView: ImageView, assetPath: String) { try { val bitmap = imageView.context.assets.open(assetPath).use { - BitmapFactory.decodeStream(it) + android.graphics.BitmapFactory.decodeStream(it) } imageView.setImageBitmap(bitmap) } catch (_: Exception) { @@ -190,8 +294,8 @@ class PayWithCardFragment : Fragment() { } fun mibCardStatusLabel(cardStatus: String): String? = when (cardStatus) { - "CHST0" -> null // Active — no badge - else -> cardStatus + "CHST0" -> null + else -> cardStatus } fun bindCardStatus(tv: TextView, statusLabel: String?) { diff --git a/app/src/main/res/layout/fragment_cards.xml b/app/src/main/res/layout/fragment_cards.xml new file mode 100644 index 0000000..2d50baa --- /dev/null +++ b/app/src/main/res/layout/fragment_cards.xml @@ -0,0 +1,194 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/item_card_stack.xml b/app/src/main/res/layout/item_card_stack.xml new file mode 100644 index 0000000..d54b8a9 --- /dev/null +++ b/app/src/main/res/layout/item_card_stack.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/drawer_menu.xml b/app/src/main/res/menu/drawer_menu.xml index 18c6288..b6e1fdb 100644 --- a/app/src/main/res/menu/drawer_menu.xml +++ b/app/src/main/res/menu/drawer_menu.xml @@ -32,9 +32,6 @@ - diff --git a/app/src/main/res/menu/more_nav_menu.xml b/app/src/main/res/menu/more_nav_menu.xml index 9aa6357..8bfd461 100644 --- a/app/src/main/res/menu/more_nav_menu.xml +++ b/app/src/main/res/menu/more_nav_menu.xml @@ -15,9 +15,6 @@ - diff --git a/app/src/main/res/values-b+dv/strings.xml b/app/src/main/res/values-b+dv/strings.xml index 5f0b9a9..112eae3 100644 --- a/app/src/main/res/values-b+dv/strings.xml +++ b/app/src/main/res/values-b+dv/strings.xml @@ -72,7 +72,8 @@ ހަރަކާތްތައް ޓްރާންސެކްޝަން ތާރީހް ފައިނޭންސް - ކާޑް ސެޓިންގ + ކާޑްތައް + ކާޑް މެނޭޖްކޮށް ފައިސާ ދައްކާ ސެޓިންގ ހުރިހާ ބޭންކް އެކައުންޓްތައް ބަލާ ޓްރާންސްފަ ކޮންޓެކްޓްތައް މެނޭޖް ކުރޭ @@ -81,8 +82,6 @@ ފަހުގެ ޓްރާންސްފަތައް ބަލާ އެކައުންޓް ތަކުގެ ޓްރާންސެކްޝަން ތާރީހް ލޯން އަދި ފައިނޭންސިންގ - ކާޑް ބޭނުންކޮށް ފައިސާ ދައްކާ - ކާޑް ސެޓިންގ މެނޭޖް ކުރޭ OTP ކޯޑް ތައްޔާރު ކުރޭ އެޕްލިކޭޝަންގެ ތަރުތީބު ނެވިގޭޝަން ހުޅުވާ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8713e9c..26d45fd 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -93,8 +93,7 @@ View your recent transfers Full transaction history by account Loans and financing overview - Make a payment using your card - Manage your card preferences + Manage and pay with your cards Generate OTP codes for authentication App preferences and configuration Open navigation @@ -311,9 +310,9 @@ %.2f%% - Pay with Card - QR Pay - NFC Pay + Cards + Scan to Pay + Tap to Pay Skill issue on MIB side, Not supported Change PIN Freeze