Compare commits
7
Commits
v1.0.2
...
1753d648bd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1753d648bd
|
||
|
|
423b0bf1e1
|
||
|
|
d713047970
|
||
|
|
d59a6fad82
|
||
|
|
8e47101401
|
||
|
|
9431a90cd0
|
||
|
|
3a10f36c39
|
@@ -64,6 +64,9 @@ dependencies {
|
|||||||
// RecyclerView for accounts list
|
// RecyclerView for accounts list
|
||||||
implementation("androidx.recyclerview:recyclerview:1.3.2")
|
implementation("androidx.recyclerview:recyclerview:1.3.2")
|
||||||
|
|
||||||
|
// CircularProgressDrawable for spinning search icons
|
||||||
|
implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0")
|
||||||
|
|
||||||
// OkHttp for API calls
|
// OkHttp for API calls
|
||||||
implementation("com.squareup.okhttp3:okhttp:4.11.0")
|
implementation("com.squareup.okhttp3:okhttp:4.11.0")
|
||||||
|
|
||||||
|
|||||||
@@ -48,7 +48,8 @@
|
|||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.home.HomeActivity"
|
android:name=".ui.home.HomeActivity"
|
||||||
android:exported="false" />
|
android:exported="false"
|
||||||
|
android:windowSoftInputMode="adjustPan" />
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.home.QrScannerActivity"
|
android:name=".ui.home.QrScannerActivity"
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class LockActivity : AppCompatActivity() {
|
|||||||
companion object {
|
companion object {
|
||||||
private const val MAX_ATTEMPTS = 5
|
private const val MAX_ATTEMPTS = 5
|
||||||
private const val LOCKOUT_MS = 30_000L
|
private const val LOCKOUT_MS = 30_000L
|
||||||
|
const val EXTRA_RESUME = "resume"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -270,8 +271,12 @@ class LockActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun proceed() {
|
private fun proceed() {
|
||||||
startActivity(Intent(this, HomeActivity::class.java))
|
if (intent.getBooleanExtra(EXTRA_RESUME, false)) {
|
||||||
finish()
|
finish()
|
||||||
|
} else {
|
||||||
|
startActivity(Intent(this, HomeActivity::class.java))
|
||||||
|
finish()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Brute-force tracking ──────────────────────────────────────────────────
|
// ── Brute-force tracking ──────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -3,17 +3,18 @@ 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.Color
|
|
||||||
import android.graphics.drawable.GradientDrawable
|
|
||||||
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.mib.MibAccount
|
import sh.sar.basedbank.api.mib.MibAccount
|
||||||
import sh.sar.basedbank.databinding.ItemAccountBinding
|
import sh.sar.basedbank.databinding.ItemAccountBinding
|
||||||
import sh.sar.basedbank.databinding.ItemCardBinding
|
import sh.sar.basedbank.databinding.ItemCardBinding
|
||||||
import sh.sar.basedbank.databinding.ItemDateHeaderBinding
|
import sh.sar.basedbank.databinding.ItemDateHeaderBinding
|
||||||
|
import sh.sar.basedbank.util.BmlDashboardParser
|
||||||
|
import sh.sar.basedbank.util.MibAccountParser
|
||||||
|
|
||||||
class AccountsAdapter(
|
class AccountsAdapter(
|
||||||
accounts: List<MibAccount>,
|
accounts: List<MibAccount>,
|
||||||
@@ -106,7 +107,11 @@ class AccountsAdapter(
|
|||||||
fun bind(account: MibAccount) {
|
fun bind(account: MibAccount) {
|
||||||
binding.tvAccountName.text = account.accountBriefName
|
binding.tvAccountName.text = account.accountBriefName
|
||||||
binding.tvAccountNumber.text = account.accountNumber
|
binding.tvAccountNumber.text = account.accountNumber
|
||||||
binding.tvPillType.text = friendlyAccountType(account.accountTypeName)
|
val label = if (account.profileType.startsWith("BML"))
|
||||||
|
BmlDashboardParser.productLabel(account.accountTypeName)
|
||||||
|
else
|
||||||
|
MibAccountParser.productLabel(account.accountTypeName)
|
||||||
|
binding.tvPillType.text = label
|
||||||
binding.tvBalance.text = "${account.currencyName} ${account.availableBalance}"
|
binding.tvBalance.text = "${account.currencyName} ${account.availableBalance}"
|
||||||
binding.root.setOnClickListener { onAccountClick(account) }
|
binding.root.setOnClickListener { onAccountClick(account) }
|
||||||
binding.root.setOnLongClickListener {
|
binding.root.setOnLongClickListener {
|
||||||
@@ -119,15 +124,10 @@ class AccountsAdapter(
|
|||||||
private inner class CardViewHolder(private val binding: ItemCardBinding) :
|
private inner class CardViewHolder(private val binding: ItemCardBinding) :
|
||||||
RecyclerView.ViewHolder(binding.root) {
|
RecyclerView.ViewHolder(binding.root) {
|
||||||
fun bind(account: MibAccount) {
|
fun bind(account: MibAccount) {
|
||||||
val brand = cardBrand(account.accountTypeName)
|
binding.ivCardBrand.setImageResource(cardBrandIcon(account.accountTypeName))
|
||||||
binding.tvCardBrand.text = brand.label
|
binding.tvCardName.text = account.accountBriefName
|
||||||
binding.tvCardBrand.background = GradientDrawable().apply {
|
binding.tvCardNumber.text = account.accountNumber
|
||||||
shape = GradientDrawable.RECTANGLE
|
binding.tvCardProduct.text = BmlDashboardParser.productLabel(account.accountTypeName)
|
||||||
cornerRadius = 100f
|
|
||||||
setColor(Color.parseColor(brand.color))
|
|
||||||
}
|
|
||||||
binding.tvCardName.text = account.accountBriefName
|
|
||||||
binding.tvCardNumber.text = account.accountNumber
|
|
||||||
binding.layoutCardBalance.visibility = View.VISIBLE
|
binding.layoutCardBalance.visibility = View.VISIBLE
|
||||||
binding.tvCardBalance.text = "${account.currencyName} ${account.availableBalance}"
|
binding.tvCardBalance.text = "${account.currencyName} ${account.availableBalance}"
|
||||||
|
|
||||||
@@ -155,29 +155,12 @@ class AccountsAdapter(
|
|||||||
Toast.makeText(context, "Account number copied", Toast.LENGTH_SHORT).show()
|
Toast.makeText(context, "Account number copied", Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun friendlyAccountType(raw: String): String {
|
private fun cardBrandIcon(productName: String): Int = when {
|
||||||
val u = raw.trim().uppercase()
|
|
||||||
return when {
|
|
||||||
u == "SAVINGS ACCOUNT" ||
|
|
||||||
u == "SAVING ACCOUNT" -> "Savings"
|
|
||||||
u == "CURRENT ACCOUNT" ||
|
|
||||||
u == "CURRENT ACCOUNT(PERSONAL)" ||
|
|
||||||
u == "CURRENT ACCOUNT(BUSINESS)" -> "Current"
|
|
||||||
u == "WADIAH RETAIL CURRENT ACCOUNT" ||
|
|
||||||
u == "WADIAH BUSINESS CURRENT ACCOUNT" -> "Islamic Current"
|
|
||||||
u == "BML ISLAMIC SAVINGS ACCOUNT" -> "Islamic Savings"
|
|
||||||
else -> raw.trim()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class Brand(val label: String, val color: String)
|
|
||||||
|
|
||||||
private fun cardBrand(productName: String): Brand = when {
|
|
||||||
productName.contains("AMEX", ignoreCase = true) ||
|
productName.contains("AMEX", ignoreCase = true) ||
|
||||||
productName.contains("AMERICAN EXPRESS", ignoreCase = true) -> Brand("AMEX", "#016FD0")
|
productName.contains("AMERICAN EXPRESS", ignoreCase = true) -> R.drawable.americanexpress
|
||||||
productName.contains("VISA", ignoreCase = true) -> Brand("VISA", "#1A1F71")
|
productName.contains("VISA", ignoreCase = true) -> R.drawable.visa
|
||||||
productName.contains("MASTERCARD", ignoreCase = true) -> Brand("MC", "#FF5F00")
|
productName.contains("MASTERCARD", ignoreCase = true) -> R.drawable.mastercard
|
||||||
else -> Brand("CARD", "#555555")
|
else -> R.drawable.ic_nav_card
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,9 @@ import android.view.ViewGroup
|
|||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
|
||||||
import sh.sar.basedbank.util.ContactsCache
|
import sh.sar.basedbank.util.ContactsCache
|
||||||
@@ -129,6 +131,22 @@ class AddContactSheetFragment : BottomSheetDialogFragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startLookupLoading() {
|
||||||
|
val spinner = CircularProgressDrawable(requireContext()).apply {
|
||||||
|
setStyle(CircularProgressDrawable.DEFAULT)
|
||||||
|
setColorSchemeColors(com.google.android.material.color.MaterialColors.getColor(
|
||||||
|
requireView(), com.google.android.material.R.attr.colorPrimary, Color.GRAY))
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
binding.tilAccount.endIconDrawable = spinner
|
||||||
|
binding.tilAccount.isEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopLookupLoading() {
|
||||||
|
binding.tilAccount.isEnabled = true
|
||||||
|
binding.tilAccount.endIconDrawable = ContextCompat.getDrawable(requireContext(), android.R.drawable.ic_menu_search)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupAccountSearch() {
|
private fun setupAccountSearch() {
|
||||||
binding.tilAccount.setEndIconOnClickListener { performLookup() }
|
binding.tilAccount.setEndIconOnClickListener { performLookup() }
|
||||||
}
|
}
|
||||||
@@ -167,7 +185,7 @@ class AddContactSheetFragment : BottomSheetDialogFragment() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.tilAccount.isEnabled = false
|
startLookupLoading()
|
||||||
binding.tilDestination.isEnabled = false
|
binding.tilDestination.isEnabled = false
|
||||||
binding.btnSave.isEnabled = false
|
binding.btnSave.isEnabled = false
|
||||||
|
|
||||||
@@ -175,7 +193,7 @@ class AddContactSheetFragment : BottomSheetDialogFragment() {
|
|||||||
val result = withContext(Dispatchers.IO) {
|
val result = withContext(Dispatchers.IO) {
|
||||||
if (dest.isBml) lookupForBml(input) else lookupForMib(dest, input)
|
if (dest.isBml) lookupForBml(input) else lookupForMib(dest, input)
|
||||||
}
|
}
|
||||||
binding.tilAccount.isEnabled = true
|
stopLookupLoading()
|
||||||
binding.tilDestination.isEnabled = true
|
binding.tilDestination.isEnabled = true
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
showLookupResult(result, input)
|
showLookupResult(result, input)
|
||||||
@@ -341,6 +359,7 @@ class AddContactSheetFragment : BottomSheetDialogFragment() {
|
|||||||
binding.tilAlias.error = null
|
binding.tilAlias.error = null
|
||||||
|
|
||||||
binding.btnSave.isEnabled = false
|
binding.btnSave.isEnabled = false
|
||||||
|
binding.btnSave.text = "Saving..."
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
val success = withContext(Dispatchers.IO) {
|
val success = withContext(Dispatchers.IO) {
|
||||||
@@ -352,6 +371,7 @@ class AddContactSheetFragment : BottomSheetDialogFragment() {
|
|||||||
dismiss()
|
dismiss()
|
||||||
} else {
|
} else {
|
||||||
binding.btnSave.isEnabled = true
|
binding.btnSave.isEnabled = true
|
||||||
|
binding.btnSave.text = "Save"
|
||||||
Toast.makeText(requireContext(), R.string.contact_save_failed, Toast.LENGTH_SHORT).show()
|
Toast.makeText(requireContext(), R.string.contact_save_failed, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -64,15 +64,22 @@ class HomeActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private val warningRunnable = Runnable { showAutolockWarning() }
|
private val warningRunnable = Runnable { showAutolockWarning() }
|
||||||
|
|
||||||
|
private var isLocked = false
|
||||||
|
|
||||||
private val autolockRunnable = Runnable {
|
private val autolockRunnable = Runnable {
|
||||||
countdownTimer?.cancel(); countdownTimer = null
|
countdownTimer?.cancel(); countdownTimer = null
|
||||||
warningDialog?.dismiss(); warningDialog = null
|
warningDialog?.dismiss(); warningDialog = null
|
||||||
val securitySet = getSharedPreferences("prefs", MODE_PRIVATE)
|
val securitySet = getSharedPreferences("prefs", MODE_PRIVATE)
|
||||||
.getString("security_method", null) != null
|
.getString("security_method", null) != null
|
||||||
if (securitySet) {
|
if (securitySet) lock()
|
||||||
startActivity(Intent(this, sh.sar.basedbank.LockActivity::class.java))
|
}
|
||||||
finish()
|
|
||||||
}
|
private fun lock() {
|
||||||
|
isLocked = true
|
||||||
|
startActivity(
|
||||||
|
Intent(this, sh.sar.basedbank.LockActivity::class.java)
|
||||||
|
.putExtra(sh.sar.basedbank.LockActivity.EXTRA_RESUME, true)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@@ -224,6 +231,13 @@ class HomeActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setBottomNavVisible(visible: Boolean) {
|
||||||
|
val isBottom = getSharedPreferences("prefs", MODE_PRIVATE).getBoolean("bottom_nav", false)
|
||||||
|
if (isBottom) {
|
||||||
|
binding.bottomNavigation.visibility = if (visible) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun setRefreshing(visible: Boolean) {
|
fun setRefreshing(visible: Boolean) {
|
||||||
binding.refreshIndicator.visibility = if (visible) View.VISIBLE else View.GONE
|
binding.refreshIndicator.visibility = if (visible) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
@@ -237,6 +251,13 @@ class HomeActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
// Returning from LockActivity — skip the elapsed check and reset state.
|
||||||
|
if (isLocked) {
|
||||||
|
isLocked = false
|
||||||
|
pauseTime = 0L
|
||||||
|
resetAutolockTimer()
|
||||||
|
return
|
||||||
|
}
|
||||||
// If we were away long enough to have hit the autolock timeout (e.g. while
|
// If we were away long enough to have hit the autolock timeout (e.g. while
|
||||||
// QrScannerActivity was in the foreground), lock immediately.
|
// QrScannerActivity was in the foreground), lock immediately.
|
||||||
if (pauseTime > 0L) {
|
if (pauseTime > 0L) {
|
||||||
@@ -244,8 +265,7 @@ class HomeActivity : AppCompatActivity() {
|
|||||||
val timeout = getSharedPreferences("prefs", MODE_PRIVATE).getLong("autolock_timeout", 60_000L)
|
val timeout = getSharedPreferences("prefs", MODE_PRIVATE).getLong("autolock_timeout", 60_000L)
|
||||||
val securitySet = getSharedPreferences("prefs", MODE_PRIVATE).getString("security_method", null) != null
|
val securitySet = getSharedPreferences("prefs", MODE_PRIVATE).getString("security_method", null) != null
|
||||||
if (timeout > 0L && elapsed >= timeout && securitySet) {
|
if (timeout > 0L && elapsed >= timeout && securitySet) {
|
||||||
startActivity(Intent(this, sh.sar.basedbank.LockActivity::class.java))
|
lock()
|
||||||
finish()
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -316,8 +336,7 @@ class HomeActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
if (item.itemId == R.id.action_lock) {
|
if (item.itemId == R.id.action_lock) {
|
||||||
startActivity(Intent(this, sh.sar.basedbank.LockActivity::class.java))
|
lock()
|
||||||
finish()
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import androidx.biometric.BiometricManager
|
|||||||
import androidx.biometric.BiometricPrompt
|
import androidx.biometric.BiometricPrompt
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.widget.addTextChangedListener
|
import androidx.core.widget.addTextChangedListener
|
||||||
|
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
||||||
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.lifecycle.lifecycleScope
|
||||||
@@ -159,6 +160,22 @@ class TransferFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun startLookupLoading() {
|
||||||
|
val spinner = CircularProgressDrawable(requireContext()).apply {
|
||||||
|
setStyle(CircularProgressDrawable.DEFAULT)
|
||||||
|
setColorSchemeColors(com.google.android.material.color.MaterialColors.getColor(
|
||||||
|
requireView(), com.google.android.material.R.attr.colorPrimary, Color.GRAY))
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
binding.tilTo.endIconDrawable = spinner
|
||||||
|
binding.tilTo.isEnabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun stopLookupLoading() {
|
||||||
|
binding.tilTo.isEnabled = true
|
||||||
|
binding.tilTo.endIconDrawable = ContextCompat.getDrawable(requireContext(), android.R.drawable.ic_menu_search)
|
||||||
|
}
|
||||||
|
|
||||||
private fun setupFromDropdown() {
|
private fun setupFromDropdown() {
|
||||||
binding.btnClearFromInfo.setOnClickListener {
|
binding.btnClearFromInfo.setOnClickListener {
|
||||||
selectedAccount = null
|
selectedAccount = null
|
||||||
@@ -313,7 +330,7 @@ class TransferFragment : Fragment() {
|
|||||||
|
|
||||||
val isBmlSource = selectedAccount?.profileType?.startsWith("BML") == true
|
val isBmlSource = selectedAccount?.profileType?.startsWith("BML") == true
|
||||||
|
|
||||||
binding.tilTo.isEnabled = false
|
startLookupLoading()
|
||||||
|
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
var errorMsg: String? = null
|
var errorMsg: String? = null
|
||||||
@@ -357,7 +374,7 @@ class TransferFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.tilTo.isEnabled = true
|
stopLookupLoading()
|
||||||
if (info != null) {
|
if (info != null) {
|
||||||
val accounts = viewModel.accounts.value ?: emptyList()
|
val accounts = viewModel.accounts.value ?: emptyList()
|
||||||
val matchedAcc = accounts.firstOrNull { it.accountNumber == info.accountNumber }
|
val matchedAcc = accounts.firstOrNull { it.accountNumber == info.accountNumber }
|
||||||
@@ -392,7 +409,7 @@ class TransferFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun lookupFahipayTarget(number: String) {
|
private fun lookupFahipayTarget(number: String) {
|
||||||
binding.tilTo.isEnabled = false
|
startLookupLoading()
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
data class LookupResult(
|
data class LookupResult(
|
||||||
val dhiraagu: DhiraaguClient.Result,
|
val dhiraagu: DhiraaguClient.Result,
|
||||||
@@ -419,7 +436,7 @@ class TransferFragment : Fragment() {
|
|||||||
LookupResult(d, o)
|
LookupResult(d, o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.tilTo.isEnabled = true
|
stopLookupLoading()
|
||||||
|
|
||||||
val dhiraaguName = result.dhiraagu.ownerName.takeIf { it.isNotBlank() }
|
val dhiraaguName = result.dhiraagu.ownerName.takeIf { it.isNotBlank() }
|
||||||
|
|
||||||
@@ -586,6 +603,7 @@ class TransferFragment : Fragment() {
|
|||||||
|
|
||||||
val doTransfer: () -> Unit = {
|
val doTransfer: () -> Unit = {
|
||||||
binding.btnTransfer.isEnabled = false
|
binding.btnTransfer.isEnabled = false
|
||||||
|
(activity as? HomeActivity)?.setRefreshing(true)
|
||||||
viewLifecycleOwner.lifecycleScope.launch {
|
viewLifecycleOwner.lifecycleScope.launch {
|
||||||
val (ok, msg, receipt) = withContext(Dispatchers.IO) {
|
val (ok, msg, receipt) = withContext(Dispatchers.IO) {
|
||||||
if (!isSrcBml) {
|
if (!isSrcBml) {
|
||||||
@@ -595,6 +613,7 @@ class TransferFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
binding.btnTransfer.isEnabled = true
|
binding.btnTransfer.isEnabled = true
|
||||||
|
(activity as? HomeActivity)?.setRefreshing(false)
|
||||||
if (ok && receipt != null) {
|
if (ok && receipt != null) {
|
||||||
clearForm()
|
clearForm()
|
||||||
val activity = requireActivity() as HomeActivity
|
val activity = requireActivity() as HomeActivity
|
||||||
|
|||||||
@@ -313,6 +313,12 @@ class TransferReceiptFragment : Fragment() {
|
|||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
requireActivity().title = "Receipt"
|
requireActivity().title = "Receipt"
|
||||||
|
(activity as? HomeActivity)?.setBottomNavVisible(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
(activity as? HomeActivity)?.setBottomNavVisible(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
|
|||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package sh.sar.basedbank.util
|
||||||
|
|
||||||
|
object BmlDashboardParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a display-ready product label for a BML dashboard account or card.
|
||||||
|
* Known BML product names are mapped to short friendly labels.
|
||||||
|
* Everything else is title-cased (first letter of each word capitalised).
|
||||||
|
*/
|
||||||
|
fun productLabel(raw: String): String {
|
||||||
|
val u = raw.trim().uppercase()
|
||||||
|
return when {
|
||||||
|
u == "SAVINGS ACCOUNT" -> "Savings"
|
||||||
|
u == "CURRENT ACCOUNT" ||
|
||||||
|
u == "CURRENT ACCOUNT(PERSONAL)" ||
|
||||||
|
u == "CURRENT ACCOUNT(BUSINESS)" -> "Current"
|
||||||
|
u == "WADIAH RETAIL CURRENT ACCOUNT" ||
|
||||||
|
u == "WADIAH BUSINESS CURRENT ACCOUNT" -> "Islamic Current"
|
||||||
|
u == "BML ISLAMIC SAVINGS ACCOUNT" -> "Islamic Savings"
|
||||||
|
else -> toTitleCase(raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun toTitleCase(input: String): String =
|
||||||
|
input.trim().lowercase().split(" ").joinToString(" ") { word ->
|
||||||
|
word.replaceFirstChar { it.uppercaseChar() }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package sh.sar.basedbank.util
|
||||||
|
|
||||||
|
object MibAccountParser {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a display-ready product label for a MIB (Faisanet) account type name.
|
||||||
|
* Known MIB accountTypeName values are mapped to short friendly labels.
|
||||||
|
* Everything else is returned trimmed as-is.
|
||||||
|
*/
|
||||||
|
fun productLabel(raw: String): String {
|
||||||
|
val u = raw.trim().uppercase()
|
||||||
|
return when {
|
||||||
|
u == "SAVING ACCOUNT" -> "Savings"
|
||||||
|
u == "CURRENT ACCOUNT" -> "Current"
|
||||||
|
else -> raw.trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#016FD0"
|
||||||
|
android:pathData="M16.015 14.378c0-.32-.135-.496-.344-.622-.21-.12-.464-.135-.81-.135h-1.543v2.82h.675v-1.027h.72c.24 0 .39.024.478.125.12.13.104.38.104.55v.35h.66v-.555c-.002-.25-.017-.376-.108-.516-.06-.08-.18-.18-.33-.234l.02-.008c.18-.072.48-.297.48-.747zm-.87.407l-.028-.002c-.09.053-.195.058-.33.058h-.81v-.63h.824c.12 0 .24 0 .33.05.098.048.156.147.15.255 0 .12-.045.215-.134.27zM20.297 15.837H19v.6h1.304c.676 0 1.05-.278 1.05-.884 0-.28-.066-.448-.187-.582-.153-.133-.392-.193-.73-.207l-.376-.015c-.104 0-.18 0-.255-.03-.09-.03-.15-.105-.15-.21 0-.09.017-.166.09-.21.083-.046.177-.066.272-.06h1.23v-.602h-1.35c-.704 0-.958.437-.958.84 0 .9.776.855 1.407.87.104 0 .18.015.225.06.046.03.082.106.082.18 0 .077-.035.15-.08.18-.06.053-.15.07-.277.07zM0 0v10.096L.81 8.22h1.75l.225.464V8.22h2.043l.45 1.02.437-1.013h6.502c.295 0 .56.057.756.236v-.23h1.787v.23c.307-.17.686-.23 1.12-.23h2.606l.24.466v-.466h1.918l.254.465v-.466h1.858v3.948H20.87l-.36-.6v.585h-2.353l-.256-.63h-.583l-.27.614h-1.213c-.48 0-.84-.104-1.08-.24v.24h-2.89v-.884c0-.12-.03-.12-.105-.135h-.105v1.036H6.067v-.48l-.21.48H4.69l-.202-.48v.465H2.235l-.256-.624H1.4l-.256.624H0V24h23.786v-7.108c-.27.135-.613.18-.973.18H21.09v-.255c-.21.165-.57.255-.914.255H14.71v-.9c0-.12-.018-.12-.12-.12h-.075v1.022h-1.8v-1.066c-.298.136-.643.15-.928.136h-.214v.915h-2.18l-.54-.617-.57.6H4.742v-3.93h3.61l.518.602.554-.6h2.412c.28 0 .74.03.942.225v-.24h2.177c.202 0 .644.045.903.225v-.24h3.265v.24c.163-.164.508-.24.803-.24h1.89v.24c.194-.15.464-.24.84-.24h1.176V0H0zM21.156 14.955c.004.005.006.012.01.016.01.01.024.01.032.02l-.042-.035zM23.828 13.082h.065v.555h-.065zM23.865 15.03v-.005c-.03-.025-.046-.048-.075-.07-.15-.153-.39-.215-.764-.225l-.36-.012c-.12 0-.194-.007-.27-.03-.09-.03-.15-.105-.15-.21 0-.09.03-.16.09-.204.076-.045.15-.05.27-.05h1.223v-.588h-1.283c-.69 0-.96.437-.96.84 0 .9.78.855 1.41.87.104 0 .18.015.224.06.046.03.076.106.076.18 0 .07-.034.138-.09.18-.045.056-.136.07-.27.07h-1.288v.605h1.287c.42 0 .734-.118.9-.36h.03c.09-.134.135-.3.135-.523 0-.24-.045-.39-.135-.526zM18.597 14.208v-.583h-2.235V16.458h2.235v-.585h-1.57v-.57h1.533v-.584h-1.532v-.51M13.51 8.787h.685V11.6h-.684zM13.126 9.543l-.007.006c0-.314-.13-.5-.34-.624-.217-.125-.47-.135-.81-.135H10.43v2.82h.674v-1.034h.72c.24 0 .39.03.487.12.122.136.107.378.107.548v.354h.677v-.553c0-.25-.016-.375-.11-.516-.09-.107-.202-.19-.33-.237.172-.07.472-.3.472-.75zm-.855.396h-.015c-.09.054-.195.056-.33.056H11.1v-.623h.825c.12 0 .24.004.33.05.09.04.15.128.15.25s-.047.22-.134.266zM15.92 9.373h.632v-.6h-.644c-.464 0-.804.105-1.02.33-.286.3-.362.69-.362 1.11 0 .512.123.833.36 1.074.232.238.645.31.97.31h.78l.255-.627h1.39l.262.627h1.36v-2.11l1.272 2.11h.95l.002.002V8.786h-.684v1.963l-1.18-1.96h-1.02V11.4L18.11 8.744h-1.004l-.943 2.22h-.3c-.177 0-.362-.03-.468-.134-.125-.15-.186-.36-.186-.662 0-.285.08-.51.194-.63.133-.135.272-.165.516-.165zm1.668-.108l.464 1.118v.002h-.93l.466-1.12zM2.38 10.97l.254.628H4V9.393l.972 2.205h.584l.973-2.202.015 2.202h.69v-2.81H6.118l-.807 1.904-.876-1.905H3.343v2.663L2.205 8.787h-.997L.01 11.597h.72l.26-.626h1.39zm-.688-1.705l.46 1.118-.003.002h-.915l.457-1.12zM11.856 13.62H9.714l-.85.923-.825-.922H5.346v2.82H8l.855-.932.824.93h1.302v-.94h.838c.6 0 1.17-.164 1.17-.945l-.006-.003c0-.78-.598-.93-1.128-.93zM7.67 15.853l-.014-.002H6.02v-.557h1.47v-.574H6.02v-.51H7.7l.733.82-.764.824zm2.642.33l-1.03-1.147 1.03-1.108v2.253zm1.553-1.258h-.885v-.717h.885c.24 0 .42.098.42.344 0 .243-.15.372-.42.372zM9.967 9.373v-.586H7.73V11.6h2.237v-.58H8.4v-.564h1.527V9.88H8.4v-.507" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF5F00"
|
||||||
|
android:pathData="M11.343 18.031c.058.049.12.098.181.146-1.177.783-2.59 1.238-4.107 1.238C3.32 19.416 0 16.096 0 12c0-4.095 3.32-7.416 7.416-7.416 1.518 0 2.931.456 4.105 1.238-.06.051-.12.098-.165.15C9.6 7.489 8.595 9.688 8.595 12c0 2.311 1.001 4.51 2.748 6.031zm5.241-13.447c-1.52 0-2.931.456-4.105 1.238.06.051.12.098.165.15C14.4 7.489 15.405 9.688 15.405 12c0 2.31-1.001 4.507-2.748 6.031-.058.049-.12.098-.181.146 1.177.783 2.588 1.238 4.107 1.238C20.68 19.416 24 16.096 24 12c0-4.094-3.32-7.416-7.416-7.416zM12 6.174c-.096.075-.189.15-.28.231C10.156 7.764 9.169 9.765 9.169 12c0 2.236.987 4.236 2.551 5.595.09.08.185.158.28.232.096-.074.189-.152.28-.232 1.563-1.359 2.551-3.359 2.551-5.595 0-2.235-.987-4.236-2.551-5.595-.09-.08-.184-.156-.28-.231z" />
|
||||||
|
</vector>
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24">
|
||||||
|
<path
|
||||||
|
android:fillColor="#1A1F71"
|
||||||
|
android:pathData="M9.112 8.262L5.97 15.758H3.92L2.374 9.775c-0.094,-0.368,-0.175,-0.503,-0.461,-0.658C1.447 8.864 0.677 8.627 0 8.479l0.046,-0.217h3.3a0.904,0.904,0,0,1,0.894,0.764l0.817 4.338 2.018,-5.102zM17.145 13.311c0.008,-1.979,-2.736,-2.088,-2.717,-2.972 0.006,-0.269 0.262,-0.555 0.822,-0.628a3.66,3.66,0,0,1,1.913,0.336l0.34,-1.59a5.207,5.207,0,0,0,-1.814,-0.333c-1.917 0,-3.266 1.02,-3.278 2.479,-0.012 1.079 0.963 1.68 1.698 2.04 0.756 0.367 1.01 0.603 1.006 0.931,-0.005 0.504,-0.602 0.725,-1.16 0.734,-0.975 0.015,-1.54,-0.263,-1.992,-0.473l-0.351 1.642c0.453 0.208 1.289 0.39 2.156 0.398 2.037 0 3.37,-1.006 3.377,-2.564zM22.206 15.758H24l-1.565,-7.496h-1.656a0.883,0.883,0,0,0,-0.826,0.55l-2.909 6.946h2.036l0.405,-1.12h2.488zM20.043 13.102l1.02,-2.815 0.588 2.815zM11.883 8.262l-1.603 7.496H8.34l1.605,-7.496z" />
|
||||||
|
</vector>
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:background="#FFFFFF">
|
android:background="?attr/colorSurface">
|
||||||
|
|
||||||
<!-- ══════════════════════════════════════════════════════════════════════ -->
|
<!-- ══════════════════════════════════════════════════════════════════════ -->
|
||||||
<!-- Renderable receipt card -->
|
<!-- Renderable receipt card -->
|
||||||
@@ -206,12 +206,6 @@
|
|||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Pushes buttons to bottom of screen -->
|
|
||||||
<View
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="0dp"
|
|
||||||
android:layout_weight="1" />
|
|
||||||
|
|
||||||
<!-- ══════════════════════════════════════════════════════════════════════ -->
|
<!-- ══════════════════════════════════════════════════════════════════════ -->
|
||||||
<!-- Action buttons — outside renderable area -->
|
<!-- Action buttons — outside renderable area -->
|
||||||
<!-- ══════════════════════════════════════════════════════════════════════ -->
|
<!-- ══════════════════════════════════════════════════════════════════════ -->
|
||||||
@@ -219,7 +213,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:background="#FFFFFF"
|
android:background="?attr/colorSurface"
|
||||||
android:paddingHorizontal="12dp"
|
android:paddingHorizontal="12dp"
|
||||||
android:paddingTop="8dp"
|
android:paddingTop="8dp"
|
||||||
android:paddingBottom="12dp">
|
android:paddingBottom="12dp">
|
||||||
|
|||||||
@@ -14,18 +14,14 @@
|
|||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
android:foreground="?attr/selectableItemBackground">
|
android:foreground="?attr/selectableItemBackground">
|
||||||
|
|
||||||
<!-- Brand chip -->
|
<!-- Brand logo -->
|
||||||
<TextView
|
<ImageView
|
||||||
android:id="@+id/tvCardBrand"
|
android:id="@+id/ivCardBrand"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="48dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="32dp"
|
||||||
android:paddingHorizontal="10dp"
|
|
||||||
android:paddingVertical="6dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:textAppearance="?attr/textAppearanceLabelSmall"
|
android:scaleType="fitCenter"
|
||||||
android:textColor="@android:color/white"
|
android:contentDescription="Card brand" />
|
||||||
android:fontFamily="monospace"
|
|
||||||
android:background="@drawable/chip_background" />
|
|
||||||
|
|
||||||
<!-- Card name + number -->
|
<!-- Card name + number -->
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
@@ -50,6 +46,14 @@
|
|||||||
android:layout_marginTop="2dp"
|
android:layout_marginTop="2dp"
|
||||||
android:fontFamily="monospace" />
|
android:fontFamily="monospace" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvCardProduct"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textAppearance="?attr/textAppearanceBodySmall"
|
||||||
|
android:textColor="?attr/colorOnSurfaceVariant"
|
||||||
|
android:layout_marginTop="2dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<!-- Status pill + balance (prepaid only) -->
|
<!-- Status pill + balance (prepaid only) -->
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
[versions]
|
[versions]
|
||||||
agp = "8.9.1"
|
agp = "8.7.3"
|
||||||
kotlin = "2.1.21"
|
kotlin = "2.1.21"
|
||||||
coreKtx = "1.10.1"
|
coreKtx = "1.10.1"
|
||||||
junit = "4.13.2"
|
junit = "4.13.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user