categorize settings to unclutter it.. might need to revert this later

This commit is contained in:
2026-05-17 20:09:09 +05:00
parent b26f9deba7
commit ecbbb3ed6b
13 changed files with 832 additions and 636 deletions
@@ -0,0 +1,75 @@
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 androidx.appcompat.app.AppCompatDelegate
import androidx.appcompat.app.AppCompatDelegate.setApplicationLocales
import androidx.core.os.LocaleListCompat
import androidx.fragment.app.Fragment
import sh.sar.basedbank.R
import sh.sar.basedbank.databinding.FragmentSettingsAppearanceBinding
class SettingsAppearanceFragment : Fragment() {
private var _binding: FragmentSettingsAppearanceBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentSettingsAppearanceBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
// Navigation mode
val isBottom = prefs.getBoolean("bottom_nav", false)
binding.navModeToggle.check(if (isBottom) R.id.btnNavBottom else R.id.btnNavDrawer)
binding.navModeToggle.addOnButtonCheckedListener { _, checkedId, isChecked ->
if (!isChecked) return@addOnButtonCheckedListener
prefs.edit().putBoolean("bottom_nav", checkedId == R.id.btnNavBottom).apply()
(activity as? HomeActivity)?.applyNavMode()
}
// Theme
val saved = prefs.getString("theme", "system")
binding.themeToggle.check(when (saved) {
"light" -> R.id.btnThemeLight
"dark" -> R.id.btnThemeDark
else -> R.id.btnThemeSystem
})
binding.themeToggle.addOnButtonCheckedListener { _, checkedId, isChecked ->
if (!isChecked) return@addOnButtonCheckedListener
val (key, mode) = when (checkedId) {
R.id.btnThemeLight -> "light" to AppCompatDelegate.MODE_NIGHT_NO
R.id.btnThemeDark -> "dark" to AppCompatDelegate.MODE_NIGHT_YES
else -> "system" to AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
}
prefs.edit().putString("theme", key).apply()
AppCompatDelegate.setDefaultNightMode(mode)
}
// Language
val currentLocales = AppCompatDelegate.getApplicationLocales()
val currentLang = if (currentLocales.isEmpty) "en" else currentLocales[0]?.language ?: "en"
binding.languageToggle.check(if (currentLang == "dv") R.id.btnLangDhivehi else R.id.btnLangEnglish)
binding.languageToggle.addOnButtonCheckedListener { _, checkedId, isChecked ->
if (!isChecked) return@addOnButtonCheckedListener
val tag = if (checkedId == R.id.btnLangDhivehi) "dv" else "en"
setApplicationLocales(LocaleListCompat.forLanguageTags(tag))
}
}
override fun onResume() {
super.onResume()
requireActivity().title = getString(R.string.settings_appearance)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
@@ -1,358 +1,51 @@
package sh.sar.basedbank.ui.home package sh.sar.basedbank.ui.home
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.ImageView
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.TextView import android.widget.TextView
import android.widget.Toast import androidx.annotation.DrawableRes
import androidx.appcompat.app.AppCompatDelegate import androidx.annotation.StringRes
import androidx.appcompat.app.AppCompatDelegate.setApplicationLocales
import androidx.biometric.BiometricManager
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.databinding.FragmentSettingsBinding
import sh.sar.basedbank.ui.onboarding.SecuritySetupFragment
import sh.sar.basedbank.util.AccountCache
import sh.sar.basedbank.util.ContactImageCache
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.ForeignLimitsCache
import sh.sar.basedbank.util.RecentsCache
class SettingsFragment : Fragment() { class SettingsFragment : Fragment() {
private var _binding: FragmentSettingsBinding? = null private data class SettingsItem(
private val binding get() = _binding!! @DrawableRes val icon: Int,
@StringRes val title: Int,
val dest: () -> Fragment
)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { private val items = listOf(
_binding = FragmentSettingsBinding.inflate(inflater, container, false) SettingsItem(R.drawable.ic_contacts, R.string.settings_logins) { SettingsLoginsFragment() },
return binding.root SettingsItem(R.drawable.ic_settings_appearance, R.string.settings_appearance) { SettingsAppearanceFragment() },
} SettingsItem(R.drawable.ic_lock, R.string.settings_privacy_security) { SettingsSecurityFragment() },
SettingsItem(R.drawable.ic_settings_storage, R.string.settings_storage) { SettingsStorageFragment() },
)
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View =
inflater.inflate(R.layout.fragment_settings, container, false)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE) val list = view.findViewById<LinearLayout>(R.id.settingsList)
val inflater = LayoutInflater.from(requireContext())
// Navigation mode for (item in items) {
val isBottom = prefs.getBoolean("bottom_nav", false) val row = inflater.inflate(R.layout.item_more_nav, list, false)
binding.navModeToggle.check(if (isBottom) R.id.btnNavBottom else R.id.btnNavDrawer) row.findViewById<ImageView>(R.id.ivIcon).setImageResource(item.icon)
binding.navModeToggle.addOnButtonCheckedListener { _, checkedId, isChecked -> row.findViewById<TextView>(R.id.tvLabel).setText(item.title)
if (!isChecked) return@addOnButtonCheckedListener row.setOnClickListener {
prefs.edit().putBoolean("bottom_nav", checkedId == R.id.btnNavBottom).apply() (requireActivity() as HomeActivity).showWithBackStack(item.dest())
(activity as? HomeActivity)?.applyNavMode()
}
// Theme
val saved = prefs.getString("theme", "system")
val initialId = when (saved) {
"light" -> R.id.btnThemeLight
"dark" -> R.id.btnThemeDark
else -> R.id.btnThemeSystem
}
binding.themeToggle.check(initialId)
binding.themeToggle.addOnButtonCheckedListener { _, checkedId, isChecked ->
if (!isChecked) return@addOnButtonCheckedListener
val (key, mode) = when (checkedId) {
R.id.btnThemeLight -> "light" to AppCompatDelegate.MODE_NIGHT_NO
R.id.btnThemeDark -> "dark" to AppCompatDelegate.MODE_NIGHT_YES
else -> "system" to AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
} }
prefs.edit().putString("theme", key).apply() list.addView(row)
AppCompatDelegate.setDefaultNightMode(mode)
}
// Language
val currentLocales = AppCompatDelegate.getApplicationLocales()
val currentLang = if (currentLocales.isEmpty) "en" else currentLocales[0]?.language ?: "en"
binding.languageToggle.check(
if (currentLang == "dv") R.id.btnLangDhivehi else R.id.btnLangEnglish
)
binding.languageToggle.addOnButtonCheckedListener { _, checkedId, isChecked ->
if (!isChecked) return@addOnButtonCheckedListener
val tag = if (checkedId == R.id.btnLangDhivehi) "dv" else "en"
setApplicationLocales(LocaleListCompat.forLanguageTags(tag))
}
// Auto-lock
val savedTimeout = prefs.getLong("autolock_timeout", 60_000L)
binding.autolockToggle.check(when (savedTimeout) {
0L -> R.id.btnAutolockOff
30_000L -> R.id.btnAutolock30s
180_000L -> R.id.btnAutolock3m
300_000L -> R.id.btnAutolock5m
else -> R.id.btnAutolock1m
})
binding.autolockToggle.addOnButtonCheckedListener { _, checkedId, isChecked ->
if (!isChecked) return@addOnButtonCheckedListener
val timeout = when (checkedId) {
R.id.btnAutolockOff -> 0L
R.id.btnAutolock30s -> 30_000L
R.id.btnAutolock3m -> 180_000L
R.id.btnAutolock5m -> 300_000L
else -> 60_000L
}
prefs.edit().putLong("autolock_timeout", timeout).apply()
(activity as? HomeActivity)?.resetAutolockTimer()
}
// Change lock
binding.btnChangeLock.setOnClickListener {
(requireActivity() as HomeActivity).showWithBackStack(
SecuritySetupFragment.newInstance(changeMode = true)
)
}
// Biometrics toggle — only show if device supports it
val canUseBiometrics = BiometricManager.from(requireContext())
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS
if (canUseBiometrics) {
binding.rowBiometrics.visibility = View.VISIBLE
binding.switchBiometrics.setOnCheckedChangeListener { _, isChecked ->
prefs.edit().putBoolean("biometrics_enabled", isChecked).apply()
}
}
// Block screenshots
val blockScreenshots = prefs.getBoolean("block_screenshots", true)
binding.switchBlockScreenshots.isChecked = blockScreenshots
applyFlagSecure(blockScreenshots)
binding.switchBlockScreenshots.setOnCheckedChangeListener { _, isChecked ->
prefs.edit().putBoolean("block_screenshots", isChecked).apply()
applyFlagSecure(isChecked)
}
// 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)
val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
if (binding.rowBiometrics.visibility == View.VISIBLE) {
binding.switchBiometrics.isChecked = prefs.getBoolean("biometrics_enabled", false)
}
buildLoginsSection()
}
override fun onDestroyView() {
super.onDestroyView()
_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()
val hasFahipay = store.hasFahipayCredentials()
binding.tvLoginsTitle.visibility = if (hasMib || hasBml || hasFahipay) 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_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) } }
)
}
}
if (hasFahipay) {
val profile = store.loadFahipayUserProfile()
val displayName = profile?.fullName?.takeIf { it.isNotBlank() } ?: getString(R.string.fahipay_name)
addLoginRow(container, R.drawable.fahipay_logo, displayName) {
showLoginDetails(
title = getString(R.string.fahipay_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?.nid.isNullOrBlank()) appendLine("${getString(R.string.login_detail_id_card)}: ${profile!!.nid}")
}.trim(),
onLogout = { confirmLogout(getString(R.string.fahipay_name)) { logoutFahipay(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 logoutFahipay(store: CredentialStore) {
val ctx = requireContext()
store.clearFahipayCredentials()
store.clearFahipaySession()
val app = requireActivity().application as BasedBankApp
app.fahipaySession = null
app.fahipayAccounts = emptyList()
clearAllCaches(ctx)
(activity as HomeActivity).relogin()
buildLoginsSection()
}
private fun applyFlagSecure(enabled: Boolean) {
val win = activity?.window ?: return
if (enabled) {
win.addFlags(android.view.WindowManager.LayoutParams.FLAG_SECURE)
} else {
win.clearFlags(android.view.WindowManager.LayoutParams.FLAG_SECURE)
}
}
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)
} }
} }
@@ -0,0 +1,211 @@
package sh.sar.basedbank.ui.home
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.Gravity
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 androidx.fragment.app.Fragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import sh.sar.basedbank.BasedBankApp
import sh.sar.basedbank.R
import sh.sar.basedbank.api.mib.TransactionCache
import sh.sar.basedbank.databinding.FragmentSettingsLoginsBinding
import sh.sar.basedbank.ui.login.LoginActivity
import sh.sar.basedbank.util.AccountCache
import sh.sar.basedbank.util.ContactImageCache
import sh.sar.basedbank.util.ContactsCache
import sh.sar.basedbank.util.CredentialStore
import sh.sar.basedbank.util.FinancingCache
import sh.sar.basedbank.util.ForeignLimitsCache
import sh.sar.basedbank.util.RecentsCache
class SettingsLoginsFragment : Fragment() {
private var _binding: FragmentSettingsLoginsBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentSettingsLoginsBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.btnAddAccount.setOnClickListener {
startActivity(Intent(requireContext(), LoginActivity::class.java))
}
}
override fun onResume() {
super.onResume()
requireActivity().title = getString(R.string.settings_logins)
buildLoginsSection()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
private fun buildLoginsSection() {
val ctx = requireContext()
val store = CredentialStore(ctx)
val container = binding.loginsContainer
container.removeAllViews()
val hasMib = store.hasMibCredentials()
val hasBml = store.hasBmlCredentials()
val hasFahipay = store.hasFahipayCredentials()
binding.tvLoginsTitle.visibility = if (hasMib || hasBml || hasFahipay) 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_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) } }
)
}
}
if (hasFahipay) {
val profile = store.loadFahipayUserProfile()
val displayName = profile?.fullName?.takeIf { it.isNotBlank() } ?: getString(R.string.fahipay_name)
addLoginRow(container, R.drawable.fahipay_logo, displayName) {
showLoginDetails(
title = getString(R.string.fahipay_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?.nid.isNullOrBlank()) appendLine("${getString(R.string.login_detail_id_card)}: ${profile!!.nid}")
}.trim(),
onLogout = { confirmLogout(getString(R.string.fahipay_name)) { logoutFahipay(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 logoutFahipay(store: CredentialStore) {
val ctx = requireContext()
store.clearFahipayCredentials(); store.clearFahipaySession()
val app = requireActivity().application as BasedBankApp
app.fahipaySession = null; app.fahipayAccounts = 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)
}
}
@@ -0,0 +1,91 @@
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 androidx.biometric.BiometricManager
import androidx.fragment.app.Fragment
import sh.sar.basedbank.R
import sh.sar.basedbank.databinding.FragmentSettingsSecurityBinding
import sh.sar.basedbank.ui.onboarding.SecuritySetupFragment
class SettingsSecurityFragment : Fragment() {
private var _binding: FragmentSettingsSecurityBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentSettingsSecurityBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val prefs = requireContext().getSharedPreferences("prefs", Context.MODE_PRIVATE)
// Change lock
binding.btnChangeLock.setOnClickListener {
(requireActivity() as HomeActivity).showWithBackStack(
SecuritySetupFragment.newInstance(changeMode = true)
)
}
// Biometrics
val canUseBiometrics = BiometricManager.from(requireContext())
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS
if (canUseBiometrics) {
binding.rowBiometrics.visibility = View.VISIBLE
binding.switchBiometrics.isChecked = prefs.getBoolean("biometrics_enabled", false)
binding.switchBiometrics.setOnCheckedChangeListener { _, isChecked ->
prefs.edit().putBoolean("biometrics_enabled", isChecked).apply()
}
}
// Auto-lock
binding.autolockToggle.check(when (prefs.getLong("autolock_timeout", 60_000L)) {
0L -> R.id.btnAutolockOff
30_000L -> R.id.btnAutolock30s
180_000L -> R.id.btnAutolock3m
300_000L -> R.id.btnAutolock5m
else -> R.id.btnAutolock1m
})
binding.autolockToggle.addOnButtonCheckedListener { _, checkedId, isChecked ->
if (!isChecked) return@addOnButtonCheckedListener
val timeout = when (checkedId) {
R.id.btnAutolockOff -> 0L
R.id.btnAutolock30s -> 30_000L
R.id.btnAutolock3m -> 180_000L
R.id.btnAutolock5m -> 300_000L
else -> 60_000L
}
prefs.edit().putLong("autolock_timeout", timeout).apply()
(activity as? HomeActivity)?.resetAutolockTimer()
}
// Block screenshots
val blockScreenshots = prefs.getBoolean("block_screenshots", true)
binding.switchBlockScreenshots.isChecked = blockScreenshots
applyFlagSecure(blockScreenshots)
binding.switchBlockScreenshots.setOnCheckedChangeListener { _, isChecked ->
prefs.edit().putBoolean("block_screenshots", isChecked).apply()
applyFlagSecure(isChecked)
}
}
override fun onResume() {
super.onResume()
requireActivity().title = getString(R.string.settings_privacy_security)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
private fun applyFlagSecure(enabled: Boolean) {
val win = activity?.window ?: return
if (enabled) win.addFlags(android.view.WindowManager.LayoutParams.FLAG_SECURE)
else win.clearFlags(android.view.WindowManager.LayoutParams.FLAG_SECURE)
}
}
@@ -0,0 +1,53 @@
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.Toast
import androidx.fragment.app.Fragment
import sh.sar.basedbank.R
import sh.sar.basedbank.api.mib.TransactionCache
import sh.sar.basedbank.databinding.FragmentSettingsStorageBinding
import sh.sar.basedbank.util.AccountCache
import sh.sar.basedbank.util.ContactImageCache
import sh.sar.basedbank.util.ContactsCache
import sh.sar.basedbank.util.FinancingCache
import sh.sar.basedbank.util.ForeignLimitsCache
import sh.sar.basedbank.util.RecentsCache
class SettingsStorageFragment : Fragment() {
private var _binding: FragmentSettingsStorageBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentSettingsStorageBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding.btnClearCache.setOnClickListener {
val ctx = requireContext()
clearAllCaches(ctx)
Toast.makeText(ctx, R.string.settings_cache_cleared, Toast.LENGTH_SHORT).show()
}
}
override fun onResume() {
super.onResume()
requireActivity().title = getString(R.string.settings_storage)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
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)
}
}
@@ -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="?attr/colorOnSurfaceVariant"
android:pathData="M12,3c-4.97,0 -9,4.03 -9,9s4.03,9 9,9c0.83,0 1.5,-0.67 1.5,-1.5 0,-0.39 -0.15,-0.74 -0.39,-1.01 -0.23,-0.26 -0.38,-0.61 -0.38,-0.99 0,-0.83 0.67,-1.5 1.5,-1.5H16c2.76,0 5,-2.24 5,-5 0,-4.42 -4.03,-8 -9,-8zM6.5,12C5.67,12 5,11.33 5,10.5S5.67,9 6.5,9 8,9.67 8,10.5 7.33,12 6.5,12zM9.5,8C8.67,8 8,7.33 8,6.5S8.67,5 9.5,5 11,5.67 11,6.5 10.33,8 9.5,8zM14.5,8C13.67,8 13,7.33 13,6.5S13.67,5 14.5,5 16,5.67 16,6.5 15.33,8 14.5,8zM17.5,12C16.67,12 16,11.33 16,10.5S16.67,9 17.5,9 19,9.67 19,10.5 18.33,12 17.5,12z" />
</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="?attr/colorOnSurfaceVariant"
android:pathData="M2,20h20v-4H2v4zM4,17h2v2H4v-2zM2,4v4h20V4H2zM6,7H4V5h2v2zM2,14h20v-4H2v4zM4,11h2v2H4v-2z" />
</vector>
+3 -304
View File
@@ -1,317 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView <ScrollView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:background="?attr/colorSurface"> android:background="?attr/colorSurface">
<LinearLayout <LinearLayout
android:id="@+id/settingsList"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="vertical"
android:padding="16dp"> android:paddingTop="8dp"
android:paddingBottom="8dp" />
<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" />
<!-- Navigation -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_navigation"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginBottom="12dp" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/navModeToggle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
app:singleSelection="true"
app:selectionRequired="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnNavDrawer"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_nav_drawer" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnNavBottom"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_nav_bottom" />
</com.google.android.material.button.MaterialButtonToggleGroup>
<!-- Theme -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/theme"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginBottom="12dp" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/themeToggle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:singleSelection="true"
app:selectionRequired="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnThemeSystem"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/theme_system" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnThemeLight"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/theme_light" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnThemeDark"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/theme_dark" />
</com.google.android.material.button.MaterialButtonToggleGroup>
<!-- Language -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/language"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginTop="24dp"
android:layout_marginBottom="12dp" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/languageToggle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:singleSelection="true"
app:selectionRequired="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnLangEnglish"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/lang_english" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnLangDhivehi"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/lang_dhivehi" />
</com.google.android.material.button.MaterialButtonToggleGroup>
<!-- Security -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_security"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginTop="24dp"
android:layout_marginBottom="12dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnChangeLock"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_change_lock" />
<LinearLayout
android:id="@+id/rowBiometrics"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="16dp"
android:visibility="gone">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_biometrics"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switchBiometrics"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_autolock"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/autolockToggle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:singleSelection="true"
app:selectionRequired="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAutolockOff"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/autolock_off" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAutolock30s"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/autolock_30s" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAutolock1m"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/autolock_1m" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAutolock3m"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/autolock_3m" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAutolock5m"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/autolock_5m" />
</com.google.android.material.button.MaterialButtonToggleGroup>
<!-- Privacy -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_privacy"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginTop="24dp"
android:layout_marginBottom="8dp" />
<LinearLayout
android:id="@+id/rowBlockScreenshots"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="4dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_block_screenshots"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_block_screenshots_desc"
android:textAppearance="?attr/textAppearanceBodySmall"
android:textColor="?attr/colorOnSurfaceVariant" />
</LinearLayout>
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switchBlockScreenshots"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp" />
</LinearLayout>
<!-- Cache -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_cache"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginTop="24dp"
android:layout_marginBottom="12dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnClearCache"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_clear_cache" />
</LinearLayout>
</ScrollView> </ScrollView>
@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_navigation"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginBottom="12dp" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/navModeToggle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
app:singleSelection="true"
app:selectionRequired="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnNavDrawer"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_nav_drawer" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnNavBottom"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_nav_bottom" />
</com.google.android.material.button.MaterialButtonToggleGroup>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/theme"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginBottom="12dp" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/themeToggle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:singleSelection="true"
app:selectionRequired="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnThemeSystem"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/theme_system" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnThemeLight"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/theme_light" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnThemeDark"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/theme_dark" />
</com.google.android.material.button.MaterialButtonToggleGroup>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/language"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginTop="24dp"
android:layout_marginBottom="12dp" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/languageToggle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:singleSelection="true"
app:selectionRequired="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnLangEnglish"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/lang_english" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnLangDhivehi"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/lang_dhivehi" />
</com.google.android.material.button.MaterialButtonToggleGroup>
</LinearLayout>
</ScrollView>
@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
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:text="@string/nav_add_account" />
</LinearLayout>
</ScrollView>
@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_security"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginBottom="12dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnChangeLock"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_change_lock" />
<LinearLayout
android:id="@+id/rowBiometrics"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginTop="16dp"
android:visibility="gone">
<TextView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/settings_biometrics"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switchBiometrics"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_autolock"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface"
android:layout_marginTop="16dp"
android:layout_marginBottom="8dp" />
<com.google.android.material.button.MaterialButtonToggleGroup
android:id="@+id/autolockToggle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:singleSelection="true"
app:selectionRequired="true">
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAutolockOff"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/autolock_off" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAutolock30s"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/autolock_30s" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAutolock1m"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/autolock_1m" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAutolock3m"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/autolock_3m" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnAutolock5m"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="@string/autolock_5m" />
</com.google.android.material.button.MaterialButtonToggleGroup>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_privacy"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginTop="24dp"
android:layout_marginBottom="8dp" />
<LinearLayout
android:id="@+id/rowBlockScreenshots"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_marginBottom="4dp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_block_screenshots"
android:textAppearance="?attr/textAppearanceBodyLarge"
android:textColor="?attr/colorOnSurface" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_block_screenshots_desc"
android:textAppearance="?attr/textAppearanceBodySmall"
android:textColor="?attr/colorOnSurfaceVariant" />
</LinearLayout>
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switchBlockScreenshots"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp" />
</LinearLayout>
</LinearLayout>
</ScrollView>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="?attr/colorSurface">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/settings_cache"
android:textAppearance="?attr/textAppearanceTitleMedium"
android:layout_marginBottom="12dp" />
<com.google.android.material.button.MaterialButton
android:id="@+id/btnClearCache"
style="@style/Widget.Material3.Button.OutlinedButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_clear_cache" />
</LinearLayout>
</ScrollView>
+3
View File
@@ -126,6 +126,9 @@
<string name="settings_navigation">Navigation</string> <string name="settings_navigation">Navigation</string>
<string name="settings_nav_drawer">Drawer</string> <string name="settings_nav_drawer">Drawer</string>
<string name="settings_nav_bottom">Bottom Bar</string> <string name="settings_nav_bottom">Bottom Bar</string>
<string name="settings_appearance">Appearance</string>
<string name="settings_privacy_security">Privacy &amp; Security</string>
<string name="settings_storage">Storage</string>
<string name="settings_logins">Logins</string> <string name="settings_logins">Logins</string>
<string name="settings_logout">Log out</string> <string name="settings_logout">Log out</string>
<string name="settings_logout_confirm_title">Log out of %s?</string> <string name="settings_logout_confirm_title">Log out of %s?</string>