add BML and MIB card freeze/unfreeze
Auto Tag on Version Change / check-version (push) Failing after 13m35s
Auto Tag on Version Change / check-version (push) Failing after 13m35s
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
package sh.sar.basedbank.api.bml
|
||||
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import okhttp3.Request
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.json.JSONObject
|
||||
import sh.sar.basedbank.api.models.BankServerException
|
||||
|
||||
data class BmlCardActionResult(
|
||||
val success: Boolean,
|
||||
val message: String
|
||||
)
|
||||
|
||||
class BmlCardClient {
|
||||
|
||||
private val client = newBmlApiClient()
|
||||
|
||||
/**
|
||||
* Freezes or unfreezes a BML card.
|
||||
* @param cardId BML card UUID (BankAccount.internalId)
|
||||
* @param action "freeze" or "unfreeze"
|
||||
*/
|
||||
fun setCardFreezeState(session: BmlSession, cardId: String, action: String): BmlCardActionResult {
|
||||
val body = JSONObject().apply {
|
||||
put("card", cardId)
|
||||
put("action", action)
|
||||
}.toString().toRequestBody("application/json".toMediaType())
|
||||
|
||||
val request = Request.Builder()
|
||||
.url("$BML_BASE_URL/api/mobile/services/card/freeze")
|
||||
.post(body)
|
||||
.header("Authorization", "Bearer ${session.accessToken}")
|
||||
.header("User-Agent", BML_USER_AGENT)
|
||||
.header("x-app-version", BML_APP_VERSION)
|
||||
.header("accept", "application/json")
|
||||
.build()
|
||||
|
||||
val resp = client.newCall(request).execute()
|
||||
val code = resp.code
|
||||
val responseBody = resp.body?.string()
|
||||
resp.close()
|
||||
if (code == 401 || code == 419) throw AuthExpiredException()
|
||||
if (code in 500..599) throw BankServerException("BML")
|
||||
return try {
|
||||
val json = JSONObject(responseBody ?: "")
|
||||
val ok = json.optBoolean("success") && json.optInt("code") == 0
|
||||
BmlCardActionResult(
|
||||
success = ok,
|
||||
message = json.optString("payload").ifBlank { json.optString("message") }
|
||||
)
|
||||
} catch (_: Exception) {
|
||||
BmlCardActionResult(success = false, message = "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,10 +7,18 @@ import okhttp3.Request
|
||||
import org.json.JSONObject
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
data class MibCardActionResult(
|
||||
val success: Boolean,
|
||||
val message: String,
|
||||
val currentStatusCode: String
|
||||
)
|
||||
|
||||
class MibCardsClient {
|
||||
|
||||
private val BASE_WV_URL = "https://faisamobilex-wv.mib.com.mv"
|
||||
|
||||
private val USER_AGENT = "Mozilla/5.0 (Linux; Android ${Build.VERSION.RELEASE}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.70 Mobile Safari/537.36"
|
||||
|
||||
private val client = OkHttpClient.Builder()
|
||||
.connectTimeout(20, TimeUnit.SECONDS)
|
||||
.readTimeout(20, TimeUnit.SECONDS)
|
||||
@@ -20,7 +28,7 @@ class MibCardsClient {
|
||||
"mbmodel=IOS-1.0; xxid=${session.xxid}; IBSID=${session.xxid}; " +
|
||||
"mbnonce=${session.nonceGenerator}; time-tracker=597"
|
||||
|
||||
fun fetchCards(session: MibSession, loginTag: String): List<MibCard> {
|
||||
fun fetchCards(session: MibSession, loginTag: String, profileId: String = ""): List<MibCard> {
|
||||
val body = FormBody.Builder()
|
||||
.add("name", "")
|
||||
.add("start", "1")
|
||||
@@ -32,7 +40,7 @@ class MibCardsClient {
|
||||
.url("$BASE_WV_URL/ajaxDebitCard/fetchCardInfos")
|
||||
.post(body)
|
||||
.header("Cookie", cookieHeader(session))
|
||||
.header("User-Agent", "Mozilla/5.0 (Linux; Android ${Build.VERSION.RELEASE}; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/129.0.6668.70 Mobile Safari/537.36")
|
||||
.header("User-Agent", USER_AGENT)
|
||||
.header("X-Requested-With", "XMLHttpRequest")
|
||||
.header("Accept", "*/*")
|
||||
.header("Origin", BASE_WV_URL)
|
||||
@@ -55,9 +63,42 @@ class MibCardsClient {
|
||||
customerId = item.optString("customerId"),
|
||||
phoneNumber = item.optString("phoneNumber"),
|
||||
cardHolderName = item.optString("cardHolderName"),
|
||||
loginTag = loginTag
|
||||
loginTag = loginTag,
|
||||
profileId = profileId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Freezes a MIB card. action = "freeze" or "unfreeze". */
|
||||
fun setCardFreezeState(session: MibSession, cardId: String, action: String, comments: String): MibCardActionResult {
|
||||
val body = FormBody.Builder()
|
||||
.add("cardId", cardId)
|
||||
.add("comments", comments)
|
||||
.build()
|
||||
|
||||
val request = Request.Builder()
|
||||
.url("$BASE_WV_URL/ajaxDebitCard/$action")
|
||||
.post(body)
|
||||
.header("Cookie", cookieHeader(session))
|
||||
.header("User-Agent", USER_AGENT)
|
||||
.header("X-Requested-With", "XMLHttpRequest")
|
||||
.header("Accept", "*/*")
|
||||
.header("Origin", BASE_WV_URL)
|
||||
.header("Referer", "$BASE_WV_URL//debitCards/manage?cardId=$cardId&dashurl=1")
|
||||
.build()
|
||||
|
||||
return client.newCall(request).execute().use { response ->
|
||||
val bodyStr = response.body?.string()
|
||||
?: return MibCardActionResult(false, "", "")
|
||||
val json = try { JSONObject(bodyStr) } catch (_: Exception) {
|
||||
return MibCardActionResult(false, "", "")
|
||||
}
|
||||
MibCardActionResult(
|
||||
success = json.optBoolean("success"),
|
||||
message = json.optString("reasonText"),
|
||||
currentStatusCode = json.optString("currentStatusCode")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,7 +55,8 @@ data class MibCard(
|
||||
val customerId: String,
|
||||
val phoneNumber: String,
|
||||
val cardHolderName: String,
|
||||
val loginTag: String
|
||||
val loginTag: String,
|
||||
val profileId: String = ""
|
||||
)
|
||||
|
||||
data class MibFinanceDeal(
|
||||
|
||||
@@ -116,7 +116,7 @@ class DashboardFragment : Fragment() {
|
||||
val credStore = CredentialStore(requireContext())
|
||||
val hidden = credStore.getHiddenDashboardCardNumbers()
|
||||
val mibItems = (viewModel.mibCards.value ?: emptyList())
|
||||
.filter { !hidden.contains(it.maskedCardNumber) }
|
||||
.filter { CardsFragment.isMibCardActive(it.cardStatus) && !hidden.contains(it.maskedCardNumber) }
|
||||
.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) && !hidden.contains(it.accountNumber) }
|
||||
|
||||
@@ -1130,14 +1130,14 @@ fun applyNavLabelVisibility() {
|
||||
val fresh = withContext(Dispatchers.IO) {
|
||||
val sess = app.bmlSessionFor(src) ?: return@withContext null
|
||||
try {
|
||||
val accounts = BmlAccountClient().fetchAccounts(sess, src.loginTag)
|
||||
val accounts = BmlAccountClient().fetchAccounts(sess, src.loginTag, src.profileName, src.profileId)
|
||||
AccountCache.saveBml(this@HomeActivity, loginId, accounts)
|
||||
val otherBml = app.bmlAccounts.filter { it.loginTag != src.loginTag }
|
||||
val otherBml = app.bmlAccounts.filter { it.loginTag != src.loginTag || it.profileId != src.profileId }
|
||||
app.bmlAccounts = otherBml + accounts
|
||||
accounts
|
||||
} catch (_: Exception) { null }
|
||||
} ?: return@launch
|
||||
val otherAccounts = current.filter { it.bank != "BML" || it.loginTag != src.loginTag }
|
||||
val otherAccounts = current.filter { it.bank != "BML" || it.loginTag != src.loginTag || it.profileId != src.profileId }
|
||||
viewModel.accounts.postValue(otherAccounts + fresh)
|
||||
} else {
|
||||
val loginId = src.loginTag.removePrefix("mib_")
|
||||
@@ -1220,7 +1220,7 @@ fun applyNavLabelVisibility() {
|
||||
for (profile in profiles) {
|
||||
try {
|
||||
flow.switchProfile(session, profile)
|
||||
for (card in client.fetchCards(session, "mib_$loginId")) {
|
||||
for (card in client.fetchCards(session, "mib_$loginId", profile.profileId)) {
|
||||
if (seen.add(card.cardId)) result += card
|
||||
}
|
||||
} catch (_: Exception) { }
|
||||
|
||||
@@ -37,12 +37,19 @@ import com.google.android.material.button.MaterialButton
|
||||
import com.google.android.material.color.MaterialColors
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
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.BmlCardClient
|
||||
import sh.sar.basedbank.api.bml.BmlTapToPayClient
|
||||
import sh.sar.basedbank.api.mib.MibCardsClient
|
||||
import sh.sar.basedbank.nfc.BmlHostCardEmulatorService
|
||||
import sh.sar.basedbank.api.mib.MibCard
|
||||
import android.text.InputType
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.textfield.TextInputEditText
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import sh.sar.basedbank.databinding.FragmentCardsBinding
|
||||
import sh.sar.basedbank.util.CardsCache
|
||||
import sh.sar.basedbank.util.CredentialStore
|
||||
@@ -63,6 +70,8 @@ class CardsFragment : Fragment() {
|
||||
private var cardWidth: Int = 0
|
||||
private var pendingQrCardNumber: String? = null
|
||||
private var isManageMode: Boolean = false
|
||||
private var managedCardKey: String? = null
|
||||
private var freezeInFlight: Boolean = false
|
||||
|
||||
private val qrLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||
if (result.resultCode != Activity.RESULT_OK) return@registerForActivityResult
|
||||
@@ -155,8 +164,14 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
|
||||
insets
|
||||
}
|
||||
|
||||
viewModel.mibCards.observe(viewLifecycleOwner) { rebuildCards() }
|
||||
viewModel.accounts.observe(viewLifecycleOwner) { rebuildCards() }
|
||||
viewModel.mibCards.observe(viewLifecycleOwner) {
|
||||
rebuildCards()
|
||||
rebindManagedCardIfNeeded()
|
||||
}
|
||||
viewModel.accounts.observe(viewLifecycleOwner) {
|
||||
rebuildCards()
|
||||
rebindManagedCardIfNeeded()
|
||||
}
|
||||
|
||||
val cached = CardsCache.load(requireContext())
|
||||
if (cached.isNotEmpty()) {
|
||||
@@ -247,20 +262,161 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
|
||||
Toast.makeText(requireContext(), R.string.work_in_progress, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
binding.btnChangePin.setOnClickListener(wip)
|
||||
binding.btnFreeze.setOnClickListener(wip)
|
||||
binding.btnFreeze.setOnClickListener {
|
||||
when (val item = cards.getOrNull(currentCardPosition)) {
|
||||
is CardItem.Bml -> confirmBmlFreezeToggle(item)
|
||||
is CardItem.Mib -> confirmMibFreezeToggle(item)
|
||||
null -> {}
|
||||
}
|
||||
}
|
||||
binding.btnBlock.setOnClickListener(wip)
|
||||
}
|
||||
|
||||
private fun confirmBmlFreezeToggle(item: CardItem.Bml) {
|
||||
if (freezeInFlight) return
|
||||
val frozen = isBmlFrozen(item.account.statusDesc)
|
||||
val titleRes = if (frozen) R.string.card_unfreeze_confirm_title else R.string.card_freeze_confirm_title
|
||||
val messageRes = if (frozen) R.string.card_unfreeze_confirm_message else R.string.card_freeze_confirm_message
|
||||
val confirmRes = if (frozen) R.string.card_action_unfreeze else R.string.card_action_freeze
|
||||
MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(titleRes)
|
||||
.setMessage(messageRes)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(confirmRes) { _, _ -> performBmlFreezeToggle(item, freeze = !frozen) }
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun confirmMibFreezeToggle(item: CardItem.Mib) {
|
||||
if (freezeInFlight) return
|
||||
val frozen = isMibCardFrozen(item.card.cardStatus)
|
||||
val titleRes = if (frozen) R.string.card_unfreeze_confirm_title else R.string.card_freeze_confirm_title
|
||||
val messageRes = if (frozen) R.string.card_unfreeze_confirm_message else R.string.card_freeze_confirm_message
|
||||
val confirmRes = if (frozen) R.string.card_action_unfreeze else R.string.card_action_freeze
|
||||
|
||||
val ctx = requireContext()
|
||||
val dp = resources.displayMetrics.density
|
||||
val inputLayout = TextInputLayout(ctx).apply {
|
||||
hint = getString(R.string.card_freeze_comments_hint)
|
||||
val pad = (16 * dp).toInt()
|
||||
setPadding(pad, pad / 2, pad, 0)
|
||||
}
|
||||
val input = TextInputEditText(ctx).apply {
|
||||
inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
|
||||
maxLines = 3
|
||||
}
|
||||
inputLayout.addView(input)
|
||||
|
||||
MaterialAlertDialogBuilder(ctx)
|
||||
.setTitle(titleRes)
|
||||
.setMessage(messageRes)
|
||||
.setView(inputLayout)
|
||||
.setNegativeButton(R.string.cancel, null)
|
||||
.setPositiveButton(confirmRes) { _, _ ->
|
||||
val comments = input.text?.toString()?.trim().orEmpty()
|
||||
performMibFreezeToggle(item, freeze = !frozen, comments = comments)
|
||||
}
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun performMibFreezeToggle(item: CardItem.Mib, freeze: Boolean, comments: String) {
|
||||
val app = requireActivity().application as BasedBankApp
|
||||
val action = if (freeze) "freeze" else "unfreeze"
|
||||
val successRes = if (freeze) R.string.card_freeze_success else R.string.card_unfreeze_success
|
||||
val loginId = item.card.loginTag.removePrefix("mib_")
|
||||
val session = app.mibSessions[loginId]
|
||||
if (session == null) {
|
||||
Toast.makeText(requireContext(), R.string.transfer_session_unavailable, Toast.LENGTH_SHORT).show()
|
||||
return
|
||||
}
|
||||
val profiles = app.mibProfilesMap[loginId] ?: emptyList()
|
||||
val ownerProfile = profiles.firstOrNull { it.profileId == item.card.profileId }
|
||||
?: profiles.firstOrNull { it.customerId == item.card.customerId }
|
||||
freezeInFlight = true
|
||||
binding.btnFreeze.isEnabled = false
|
||||
(activity as? HomeActivity)?.setRefreshing(true)
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
app.mibMutex.withLock {
|
||||
if (ownerProfile != null) {
|
||||
app.mibFlowFor(loginId).switchProfile(session, ownerProfile)
|
||||
}
|
||||
MibCardsClient().setCardFreezeState(session, item.card.cardId, action, comments)
|
||||
}
|
||||
}
|
||||
}
|
||||
freezeInFlight = false
|
||||
if (!isAdded || _binding == null) {
|
||||
(activity as? HomeActivity)?.setRefreshing(false)
|
||||
return@launch
|
||||
}
|
||||
binding.btnFreeze.isEnabled = true
|
||||
(activity as? HomeActivity)?.setRefreshing(false)
|
||||
val response = result.getOrNull()
|
||||
if (response?.success == true) {
|
||||
Toast.makeText(requireContext(), successRes, Toast.LENGTH_SHORT).show()
|
||||
(activity as? HomeActivity)?.triggerRefreshCards()
|
||||
} else {
|
||||
val msg = response?.message?.takeIf { it.isNotBlank() }
|
||||
?: result.exceptionOrNull()?.message
|
||||
?: getString(R.string.card_freeze_failed)
|
||||
Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun performBmlFreezeToggle(item: CardItem.Bml, freeze: Boolean) {
|
||||
val app = requireActivity().application as BasedBankApp
|
||||
val action = if (freeze) "freeze" else "unfreeze"
|
||||
val successRes = if (freeze) R.string.card_freeze_success else R.string.card_unfreeze_success
|
||||
freezeInFlight = true
|
||||
binding.btnFreeze.isEnabled = false
|
||||
(activity as? HomeActivity)?.setRefreshing(true)
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
val session = app.bmlSessionFor(item.account)
|
||||
if (session == null) {
|
||||
freezeInFlight = false
|
||||
if (_binding != null) binding.btnFreeze.isEnabled = true
|
||||
(activity as? HomeActivity)?.setRefreshing(false)
|
||||
Toast.makeText(requireContext(), R.string.transfer_session_unavailable, Toast.LENGTH_SHORT).show()
|
||||
return@launch
|
||||
}
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
runCatching { BmlCardClient().setCardFreezeState(session, item.account.internalId, action) }
|
||||
}
|
||||
freezeInFlight = false
|
||||
if (!isAdded || _binding == null) {
|
||||
(activity as? HomeActivity)?.setRefreshing(false)
|
||||
return@launch
|
||||
}
|
||||
binding.btnFreeze.isEnabled = true
|
||||
(activity as? HomeActivity)?.setRefreshing(false)
|
||||
val response = result.getOrNull()
|
||||
if (response?.success == true) {
|
||||
Toast.makeText(requireContext(), successRes, Toast.LENGTH_SHORT).show()
|
||||
(activity as? HomeActivity)?.refreshBalances(item.account)
|
||||
} else {
|
||||
val msg = response?.message?.takeIf { it.isNotBlank() }
|
||||
?: result.exceptionOrNull()?.message
|
||||
?: getString(R.string.card_freeze_failed)
|
||||
Toast.makeText(requireContext(), msg, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setManageMode(enabled: Boolean) {
|
||||
isManageMode = enabled
|
||||
if (!enabled) managedCardKey = null
|
||||
requireActivity().title = getString(if (enabled) R.string.card_manage else R.string.nav_pay_with_card)
|
||||
if (enabled) enterManageMode() else exitManageMode()
|
||||
}
|
||||
|
||||
private fun enterManageMode() {
|
||||
val item = cards.getOrNull(currentCardPosition) ?: return
|
||||
private fun cardItemKey(item: CardItem): String = when (item) {
|
||||
is CardItem.Bml -> "bml:${item.account.accountNumber}"
|
||||
is CardItem.Mib -> "mib:${item.card.cardId}"
|
||||
}
|
||||
|
||||
// Bind card data
|
||||
private fun bindManageCardData(item: CardItem) {
|
||||
val cv = binding.manageCardView
|
||||
when (item) {
|
||||
is CardItem.Mib -> {
|
||||
@@ -270,7 +426,7 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
|
||||
if (assetPath != null) loadCardImage(cv.ivCardImage, assetPath)
|
||||
else cv.ivCardImage.setImageDrawable(null)
|
||||
bindCardStatus(cv.tvCardStatus, mibCardStatusLabel(item.card.cardStatus))
|
||||
cv.root.alpha = 1f
|
||||
cv.root.alpha = if (isMibCardActive(item.card.cardStatus)) 1f else 0.45f
|
||||
}
|
||||
is CardItem.Bml -> {
|
||||
cv.tvCardOwner.text = item.account.accountBriefName
|
||||
@@ -281,6 +437,37 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
|
||||
cv.root.alpha = if (isActive) 1f else 0.45f
|
||||
}
|
||||
}
|
||||
val isFrozen = when (item) {
|
||||
is CardItem.Bml -> isBmlFrozen(item.account.statusDesc)
|
||||
is CardItem.Mib -> isMibCardFrozen(item.card.cardStatus)
|
||||
}
|
||||
binding.btnFreeze.setText(if (isFrozen) R.string.card_action_unfreeze else R.string.card_action_freeze)
|
||||
// MIB doesn't allow change PIN / block while a card is frozen; BML still does.
|
||||
val mibFrozen = item is CardItem.Mib && isMibCardFrozen(item.card.cardStatus)
|
||||
binding.btnChangePin.isEnabled = !mibFrozen
|
||||
binding.btnBlock.isEnabled = !mibFrozen
|
||||
}
|
||||
|
||||
private fun rebindManagedCardIfNeeded() {
|
||||
if (!isManageMode) return
|
||||
val key = managedCardKey ?: return
|
||||
val newPos = cards.indexOfFirst { cardItemKey(it) == key }
|
||||
if (newPos < 0) return
|
||||
if (newPos != currentCardPosition) {
|
||||
currentCardPosition = newPos
|
||||
binding.rvCards.scrollToPosition(newPos)
|
||||
}
|
||||
bindManageCardData(cards[newPos])
|
||||
}
|
||||
|
||||
private fun isBmlFrozen(statusDesc: String): Boolean =
|
||||
statusDesc.equals("Block Plastic", ignoreCase = true)
|
||||
|
||||
private fun enterManageMode() {
|
||||
val item = cards.getOrNull(currentCardPosition) ?: return
|
||||
managedCardKey = cardItemKey(item)
|
||||
|
||||
bindManageCardData(item)
|
||||
|
||||
// Capture positions BEFORE layout changes (for enter animation + exit animation later)
|
||||
val contentLoc = IntArray(2).also { binding.contentLayout.getLocationOnScreen(it) }
|
||||
@@ -716,7 +903,9 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
|
||||
.map { CardItem.Bml(it) }
|
||||
val bmlActive = bmlItems.filter { it.account.statusDesc.equals("Active", ignoreCase = true) }
|
||||
val bmlInactive = bmlItems.filter { !it.account.statusDesc.equals("Active", ignoreCase = true) }
|
||||
val all: List<CardItem> = bmlActive + mibItems + bmlInactive
|
||||
val mibActive = mibItems.filter { isMibCardActive(it.card.cardStatus) }
|
||||
val mibInactive = mibItems.filter { !isMibCardActive(it.card.cardStatus) }
|
||||
val all: List<CardItem> = bmlActive + mibActive + bmlInactive + mibInactive
|
||||
// Move default BML card to front
|
||||
cards = if (defaultNum != null) {
|
||||
val def = all.filterIsInstance<CardItem.Bml>().firstOrNull { it.account.accountNumber == defaultNum }
|
||||
@@ -903,7 +1092,7 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
|
||||
if (assetPath != null) loadCardImage(ivCardImage, assetPath)
|
||||
else ivCardImage.setImageDrawable(null)
|
||||
bindCardStatus(tvCardStatus, mibCardStatusLabel(item.card.cardStatus))
|
||||
itemView.alpha = 1f
|
||||
itemView.alpha = if (isMibCardActive(item.card.cardStatus)) 1f else 0.45f
|
||||
}
|
||||
is CardItem.Bml -> {
|
||||
tvCardOwner.text = item.account.accountBriefName
|
||||
@@ -1038,9 +1227,13 @@ ViewCompat.setOnApplyWindowInsetsListener(binding.contentLayout) { v, insets ->
|
||||
|
||||
fun mibCardStatusLabel(cardStatus: String): String? = when (cardStatus) {
|
||||
"CHST0" -> null
|
||||
"CHST20" -> "Temporary blocked by client"
|
||||
else -> cardStatus
|
||||
}
|
||||
|
||||
fun isMibCardActive(cardStatus: String): Boolean = cardStatus == "CHST0"
|
||||
fun isMibCardFrozen(cardStatus: String): Boolean = cardStatus == "CHST20"
|
||||
|
||||
fun bindCardStatus(tv: TextView, statusLabel: String?) {
|
||||
if (statusLabel == null) { tv.visibility = View.GONE; return }
|
||||
tv.visibility = View.VISIBLE
|
||||
|
||||
@@ -23,6 +23,7 @@ object CardsCache {
|
||||
put("phoneNumber", c.phoneNumber)
|
||||
put("cardHolderName", c.cardHolderName)
|
||||
put("loginTag", c.loginTag)
|
||||
put("profileId", c.profileId)
|
||||
})
|
||||
}
|
||||
context.getSharedPreferences(PREFS, Context.MODE_PRIVATE)
|
||||
@@ -45,7 +46,8 @@ object CardsCache {
|
||||
customerId = o.optString("customerId"),
|
||||
phoneNumber = o.optString("phoneNumber"),
|
||||
cardHolderName = o.optString("cardHolderName"),
|
||||
loginTag = o.optString("loginTag")
|
||||
loginTag = o.optString("loginTag"),
|
||||
profileId = o.optString("profileId")
|
||||
)
|
||||
}
|
||||
} catch (_: Exception) { emptyList() }
|
||||
|
||||
Reference in New Issue
Block a user