add logout and move add account to settings page

This commit is contained in:
2026-05-15 14:55:01 +05:00
parent e8469a8888
commit 00a5edf539
6 changed files with 247 additions and 18 deletions
@@ -87,7 +87,6 @@ class HomeActivity : AppCompatActivity() {
binding.drawerLayout.closeDrawers() binding.drawerLayout.closeDrawers()
when (item.itemId) { when (item.itemId) {
R.id.nav_dashboard -> show(DashboardFragment()) R.id.nav_dashboard -> show(DashboardFragment())
R.id.nav_add_account -> startActivity(Intent(this, LoginActivity::class.java))
R.id.nav_accounts -> show(AccountsFragment()) R.id.nav_accounts -> show(AccountsFragment())
R.id.nav_contacts -> show(ContactsFragment()) R.id.nav_contacts -> show(ContactsFragment())
R.id.nav_transfer_history -> show(TransferHistoryFragment()) R.id.nav_transfer_history -> show(TransferHistoryFragment())
@@ -225,6 +224,7 @@ class HomeActivity : AppCompatActivity() {
private fun showAutolockWarning() { private fun showAutolockWarning() {
if (warningDialog?.isShowing == true) return if (warningDialog?.isShowing == true) return
if (isFinishing || isDestroyed) return
val dialog = MaterialAlertDialogBuilder(this) val dialog = MaterialAlertDialogBuilder(this)
.setTitle(R.string.autolock_warning_title) .setTitle(R.string.autolock_warning_title)
.setMessage(getString(R.string.autolock_warning_message, 10)) .setMessage(getString(R.string.autolock_warning_message, 10))
@@ -268,6 +268,25 @@ class HomeActivity : AppCompatActivity() {
} }
fun relogin() {
val store = CredentialStore(this)
val hasMib = store.hasMibCredentials()
val hasBml = store.hasBmlCredentials()
if (!hasMib && !hasBml) {
startActivity(Intent(this, LoginActivity::class.java))
finish()
return
}
// Immediately drop accounts for logged-out banks from the displayed list
val current = viewModel.accounts.value ?: emptyList()
viewModel.accounts.value = current.filter { acc ->
if (!hasMib && !acc.profileType.startsWith("BML")) return@filter false
if (!hasBml && acc.profileType.startsWith("BML")) return@filter false
true
}
autoRefresh(store.loadMibCredentials(), store.loadBmlCredentials(), store)
}
private fun autoRefresh( private fun autoRefresh(
mibCreds: CredentialStore.MibCredentials?, mibCreds: CredentialStore.MibCredentials?,
bmlCreds: CredentialStore.BmlCredentials?, bmlCreds: CredentialStore.BmlCredentials?,
@@ -2,15 +2,22 @@ package sh.sar.basedbank.ui.home
import android.content.Context import android.content.Context
import android.os.Bundle import android.os.Bundle
import android.view.Gravity
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.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.setApplicationLocales import androidx.appcompat.app.AppCompatDelegate.setApplicationLocales
import androidx.biometric.BiometricManager import androidx.biometric.BiometricManager
import androidx.core.os.LocaleListCompat import androidx.core.os.LocaleListCompat
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import android.content.Intent
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import sh.sar.basedbank.BasedBankApp
import sh.sar.basedbank.R import sh.sar.basedbank.R
import sh.sar.basedbank.api.mib.TransactionCache import sh.sar.basedbank.api.mib.TransactionCache
import sh.sar.basedbank.databinding.FragmentSettingsBinding import sh.sar.basedbank.databinding.FragmentSettingsBinding
@@ -18,6 +25,8 @@ import sh.sar.basedbank.ui.onboarding.SecuritySetupFragment
import sh.sar.basedbank.util.AccountCache import sh.sar.basedbank.util.AccountCache
import sh.sar.basedbank.util.ContactImageCache import sh.sar.basedbank.util.ContactImageCache
import sh.sar.basedbank.util.ContactsCache import sh.sar.basedbank.util.ContactsCache
import sh.sar.basedbank.util.CredentialStore
import sh.sar.basedbank.ui.login.LoginActivity
import sh.sar.basedbank.util.FinancingCache import sh.sar.basedbank.util.FinancingCache
import sh.sar.basedbank.util.ForeignLimitsCache import sh.sar.basedbank.util.ForeignLimitsCache
import sh.sar.basedbank.util.RecentsCache import sh.sar.basedbank.util.RecentsCache
@@ -90,19 +99,6 @@ class SettingsFragment : Fragment() {
(activity as? HomeActivity)?.resetAutolockTimer() (activity as? HomeActivity)?.resetAutolockTimer()
} }
// Clear cache
binding.btnClearCache.setOnClickListener {
val ctx = requireContext()
AccountCache.clear(ctx)
ContactsCache.clear(ctx)
FinancingCache.clear(ctx)
ForeignLimitsCache.clear(ctx)
RecentsCache.clear(ctx)
TransactionCache.clearAll(ctx)
ContactImageCache.clearAll(ctx)
Toast.makeText(ctx, R.string.settings_cache_cleared, Toast.LENGTH_SHORT).show()
}
// Change lock // Change lock
binding.btnChangeLock.setOnClickListener { binding.btnChangeLock.setOnClickListener {
(requireActivity() as HomeActivity).showWithBackStack( (requireActivity() as HomeActivity).showWithBackStack(
@@ -119,20 +115,187 @@ class SettingsFragment : Fragment() {
prefs.edit().putBoolean("biometrics_enabled", isChecked).apply() prefs.edit().putBoolean("biometrics_enabled", isChecked).apply()
} }
} }
// Add account
binding.btnAddAccount.setOnClickListener {
startActivity(Intent(requireContext(), LoginActivity::class.java))
}
// Clear cache
binding.btnClearCache.setOnClickListener {
val ctx = requireContext()
clearAllCaches(ctx)
Toast.makeText(ctx, R.string.settings_cache_cleared, Toast.LENGTH_SHORT).show()
}
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
requireActivity().title = getString(R.string.nav_settings) requireActivity().title = getString(R.string.nav_settings)
// Re-read biometrics pref in case it was changed by SecuritySetupFragment
val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE) val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
if (binding.rowBiometrics.visibility == View.VISIBLE) { if (binding.rowBiometrics.visibility == View.VISIBLE) {
binding.switchBiometrics.isChecked = prefs.getBoolean("biometrics_enabled", false) binding.switchBiometrics.isChecked = prefs.getBoolean("biometrics_enabled", false)
} }
buildLoginsSection()
} }
override fun onDestroyView() { override fun onDestroyView() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
} }
// ── Logins section ───────────────────────────────────────────────────────
private fun buildLoginsSection() {
val ctx = requireContext()
val store = CredentialStore(ctx)
val container = binding.loginsContainer
container.removeAllViews()
val hasMib = store.hasMibCredentials()
val hasBml = store.hasBmlCredentials()
binding.tvLoginsTitle.visibility = if (hasMib || hasBml) View.VISIBLE else View.GONE
if (hasMib) {
val profile = store.loadMibUserProfile()
val displayName = profile?.fullName?.takeIf { it.isNotBlank() } ?: getString(R.string.mib_name)
val profileNames = AccountCache.load(ctx)
.map { it.profileName }.filter { it.isNotBlank() }.distinct()
addLoginRow(container, R.drawable.mib_faisanet_logo, displayName) {
showLoginDetails(
title = getString(R.string.mib_name),
details = buildString {
if (!profile?.fullName.isNullOrBlank()) appendLine("${getString(R.string.login_detail_name)}: ${profile!!.fullName}")
if (!profile?.email.isNullOrBlank()) appendLine("${getString(R.string.login_detail_email)}: ${profile!!.email}")
if (!profile?.mobile.isNullOrBlank()) appendLine("${getString(R.string.login_detail_mobile)}: ${profile!!.mobile}")
if (profileNames.isNotEmpty()) {
appendLine()
appendLine(getString(R.string.login_detail_profiles))
profileNames.forEach { appendLine("$it") }
}
}.trim(),
onLogout = { confirmLogout(getString(R.string.mib_name)) { logoutMib(store) } }
)
}
}
if (hasBml) {
val profile = store.loadBmlUserProfile()
val displayName = profile?.fullName?.takeIf { it.isNotBlank() } ?: getString(R.string.bml_name)
val profileNames = AccountCache.loadBml(ctx)
.map { it.profileName }.filter { it.isNotBlank() }.distinct()
addLoginRow(container, R.drawable.bml_logo_vector, displayName) {
showLoginDetails(
title = getString(R.string.bml_name),
details = buildString {
if (!profile?.fullName.isNullOrBlank()) appendLine("${getString(R.string.login_detail_name)}: ${profile!!.fullName}")
if (!profile?.email.isNullOrBlank()) appendLine("${getString(R.string.login_detail_email)}: ${profile!!.email}")
if (!profile?.mobile.isNullOrBlank()) appendLine("${getString(R.string.login_detail_mobile)}: ${profile!!.mobile}")
if (!profile?.customerId.isNullOrBlank()) appendLine("${getString(R.string.login_detail_customer_id)}: ${profile!!.customerId}")
if (!profile?.idCard.isNullOrBlank()) appendLine("${getString(R.string.login_detail_id_card)}: ${profile!!.idCard}")
if (profileNames.isNotEmpty()) {
appendLine()
appendLine(getString(R.string.login_detail_profiles))
profileNames.forEach { appendLine("$it") }
}
}.trim(),
onLogout = { confirmLogout(getString(R.string.bml_name)) { logoutBml(store) } }
)
}
}
}
private fun addLoginRow(
container: LinearLayout,
logoRes: Int,
displayName: String,
onClick: () -> Unit
) {
val ctx = requireContext()
val dp = ctx.resources.displayMetrics.density
val row = LinearLayout(ctx).apply {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL
setPadding(0, (12 * dp).toInt(), 0, (12 * dp).toInt())
isClickable = true
isFocusable = true
val ta = ctx.obtainStyledAttributes(intArrayOf(android.R.attr.selectableItemBackground))
background = ta.getDrawable(0)
ta.recycle()
setOnClickListener { onClick() }
}
val logo = ImageView(ctx).apply {
setImageResource(logoRes)
scaleType = ImageView.ScaleType.CENTER_INSIDE
layoutParams = LinearLayout.LayoutParams((36 * dp).toInt(), (36 * dp).toInt()).apply {
marginEnd = (12 * dp).toInt()
}
}
val tvName = TextView(ctx).apply {
text = displayName
setTextAppearance(com.google.android.material.R.style.TextAppearance_Material3_BodyLarge)
layoutParams = LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT, 1f)
}
row.addView(logo)
row.addView(tvName)
container.addView(row)
}
private fun showLoginDetails(title: String, details: String, onLogout: () -> Unit) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(title)
.apply { if (details.isNotBlank()) setMessage(details) }
.setPositiveButton(R.string.close, null)
.setNegativeButton(R.string.settings_logout) { _, _ -> onLogout() }
.show()
}
private fun confirmLogout(bankName: String, onConfirm: () -> Unit) {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.settings_logout_confirm_title, bankName))
.setMessage(R.string.settings_logout_confirm_message)
.setPositiveButton(R.string.settings_logout) { _, _ -> onConfirm() }
.setNegativeButton(R.string.cancel, null)
.show()
}
private fun logoutMib(store: CredentialStore) {
val ctx = requireContext()
store.clearMibCredentials()
ctx.getSharedPreferences("mib_prefs", Context.MODE_PRIVATE).edit().clear().apply()
val app = requireActivity().application as BasedBankApp
app.accounts = emptyList()
app.mibSession = null
app.mibProfiles = emptyList()
clearAllCaches(ctx)
(activity as HomeActivity).relogin()
buildLoginsSection()
}
private fun logoutBml(store: CredentialStore) {
val ctx = requireContext()
store.clearBmlCredentials()
store.clearBmlSession()
val app = requireActivity().application as BasedBankApp
app.bmlSession = null
app.bmlAccounts = emptyList()
clearAllCaches(ctx)
(activity as HomeActivity).relogin()
buildLoginsSection()
}
private fun clearAllCaches(ctx: Context) {
AccountCache.clear(ctx)
ContactsCache.clear(ctx)
FinancingCache.clear(ctx)
ForeignLimitsCache.clear(ctx)
RecentsCache.clear(ctx)
TransactionCache.clearAll(ctx)
ContactImageCache.clearAll(ctx)
}
} }
@@ -12,6 +12,30 @@
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp"> android:padding="16dp">
<TextView
android:id="@+id/tvLoginsTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_logins"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginBottom="8dp"
android:visibility="gone" />
<LinearLayout
android:id="@+id/loginsContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAddAccount"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="24dp"
android:text="@string/nav_add_account" />
<TextView <TextView
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
-3
View File
@@ -29,9 +29,6 @@
</group> </group>
<group android:id="@+id/group_system" android:checkableBehavior="single"> <group android:id="@+id/group_system" android:checkableBehavior="single">
<item android:id="@+id/nav_add_account"
android:icon="@drawable/ic_nav_add_account"
android:title="@string/nav_add_account" />
<item android:id="@+id/nav_otp" <item android:id="@+id/nav_otp"
android:icon="@drawable/ic_nav_generic" android:icon="@drawable/ic_nav_generic"
android:title="@string/nav_otp" /> android:title="@string/nav_otp" />
+13
View File
@@ -92,6 +92,19 @@
<string name="settings_cache">ކޭޝް</string> <string name="settings_cache">ކޭޝް</string>
<string name="settings_clear_cache">ކޭޝް ސާފުކުރޭ</string> <string name="settings_clear_cache">ކޭޝް ސާފުކުރޭ</string>
<string name="settings_cache_cleared">ކޭޝް ސާފުކުރެވިއްޖެ</string> <string name="settings_cache_cleared">ކޭޝް ސާފުކުރެވިއްޖެ</string>
<string name="settings_logins">ލޮގިންތައް</string>
<string name="settings_logout">ލޮގްއައުޓް</string>
<string name="settings_logout_confirm_title">%s އިން ލޮގްއައުޓް ވަންތަ؟</string>
<string name="settings_logout_confirm_message">ހުރިހާ ކޭޝް ޑޭޓާ ސާފުވެ، ބާކީ ހުރި އެކައުންޓްތައް އަލުން ލޯޑްވާނެ.</string>
<string name="login_detail_name">ނަން</string>
<string name="login_detail_username">ޔޫޒަރ ނޭމް</string>
<string name="login_detail_email">އީމެއިލް</string>
<string name="login_detail_mobile">މޮބައިލް</string>
<string name="login_detail_customer_id">ކަސްޓަމަ ID</string>
<string name="login_detail_id_card">ID ކާޑް</string>
<string name="login_detail_profiles">ޕްރޮފައިލްތައް</string>
<string name="close">ބަންދު</string>
<string name="cancel">ކެންސަލް</string>
<!-- Home --> <!-- Home -->
<string name="accounts">އެކައުންޓްތައް</string> <string name="accounts">އެކައުންޓްތައް</string>
+13
View File
@@ -110,6 +110,19 @@
<string name="settings_cache">Cache</string> <string name="settings_cache">Cache</string>
<string name="settings_clear_cache">Clear Cache</string> <string name="settings_clear_cache">Clear Cache</string>
<string name="settings_cache_cleared">Cache cleared</string> <string name="settings_cache_cleared">Cache cleared</string>
<string name="settings_logins">Logins</string>
<string name="settings_logout">Log out</string>
<string name="settings_logout_confirm_title">Log out of %s?</string>
<string name="settings_logout_confirm_message">All cached data will be cleared and remaining accounts will be refreshed.</string>
<string name="login_detail_name">Name</string>
<string name="login_detail_username">Username</string>
<string name="login_detail_email">Email</string>
<string name="login_detail_mobile">Mobile</string>
<string name="login_detail_customer_id">Customer ID</string>
<string name="login_detail_id_card">ID Card</string>
<string name="login_detail_profiles">Profiles</string>
<string name="close">Close</string>
<string name="cancel">Cancel</string>
<!-- Home --> <!-- Home -->
<string name="transfer_same_account">This is your source account</string> <string name="transfer_same_account">This is your source account</string>