diff --git a/app/src/main/java/sh/sar/basedbank/api/mib/MibTransferClient.kt b/app/src/main/java/sh/sar/basedbank/api/mib/MibTransferClient.kt
index 6609efe..0458f94 100644
--- a/app/src/main/java/sh/sar/basedbank/api/mib/MibTransferClient.kt
+++ b/app/src/main/java/sh/sar/basedbank/api/mib/MibTransferClient.kt
@@ -68,6 +68,7 @@ class MibTransferClient {
.withWvHeaders(session)
.build()
return client.newCall(request).execute().use { response ->
+ if (response.code == 419) throw SessionExpiredException()
val bodyStr = response.body?.string() ?: ""
val json = try { JSONObject(bodyStr) } catch (_: Exception) { null }
if (json == null || !json.optBoolean("success")) {
diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/AccountHistoryFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/AccountHistoryFragment.kt
index 6d7dd6e..2f48560 100644
--- a/app/src/main/java/sh/sar/basedbank/ui/home/AccountHistoryFragment.kt
+++ b/app/src/main/java/sh/sar/basedbank/ui/home/AccountHistoryFragment.kt
@@ -118,6 +118,14 @@ class AccountHistoryFragment : Fragment() {
}
(activity as? HomeActivity)?.setRefreshing(true)
loadNextPage()
+
+ binding.swipeRefresh.setOnRefreshListener {
+ if (isLoading) {
+ binding.swipeRefresh.isRefreshing = false
+ } else {
+ resetAndReload()
+ }
+ }
}
override fun onResume() {
@@ -135,6 +143,17 @@ class AccountHistoryFragment : Fragment() {
binding.emptyView.visibility = if (filtered.isEmpty() && !isLoading) View.VISIBLE else View.GONE
}
+ private fun resetAndReload() {
+ allTransactions.clear()
+ pendingImageNames.clear()
+ pendingIconUrls.clear()
+ firstPageDone = false
+ fetcher = HistoryFetcher(account)
+ adapter.setTransactions(emptyList())
+ binding.emptyView.visibility = View.GONE
+ loadNextPage()
+ }
+
private fun loadNextPage() {
if (isLoading || !fetcher.hasMore()) return
isLoading = true
@@ -153,6 +172,7 @@ class AccountHistoryFragment : Fragment() {
if (!firstPageDone) {
firstPageDone = true
(activity as? HomeActivity)?.setRefreshing(false)
+ binding.swipeRefresh.isRefreshing = false
}
if (transactions.isNotEmpty()) {
diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/AccountsFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/AccountsFragment.kt
index 00fdf42..7f491d2 100644
--- a/app/src/main/java/sh/sar/basedbank/ui/home/AccountsFragment.kt
+++ b/app/src/main/java/sh/sar/basedbank/ui/home/AccountsFragment.kt
@@ -45,6 +45,11 @@ class AccountsFragment : Fragment() {
viewModel.accounts.observe(viewLifecycleOwner) { adapter.updateAccounts(it) }
viewModel.hideAmounts.observe(viewLifecycleOwner) { adapter.setHideAmounts(it) }
+
+ binding.swipeRefresh.setOnRefreshListener {
+ (activity as? HomeActivity)?.triggerRefresh()
+ binding.swipeRefresh.isRefreshing = false
+ }
}
override fun onResume() {
diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/ContactsFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/ContactsFragment.kt
index a005021..97298f1 100644
--- a/app/src/main/java/sh/sar/basedbank/ui/home/ContactsFragment.kt
+++ b/app/src/main/java/sh/sar/basedbank/ui/home/ContactsFragment.kt
@@ -48,6 +48,7 @@ class ContactsFragment : Fragment() {
private var currentSearch: String = ""
private var mediator: TabLayoutMediator? = null
private lateinit var pagerAdapter: ContactsPagerAdapter
+ private var contactsRefreshing = false
private data class TabPage(val categoryId: String?, val label: String)
@@ -134,6 +135,11 @@ class ContactsFragment : Fragment() {
(activity as? HomeActivity)?.loadAllContacts()
+ binding.swipeRefresh.setOnRefreshListener {
+ contactsRefreshing = true
+ (activity as? HomeActivity)?.loadAllContacts()
+ }
+
viewModel.contactCategories.observe(viewLifecycleOwner) { cats ->
rebuildPager(cats)
}
@@ -143,6 +149,10 @@ class ContactsFragment : Fragment() {
pagerAdapter.updateContacts(allContacts)
binding.emptyView.visibility = if (contacts.isEmpty()) View.VISIBLE else View.GONE
binding.loadingView.visibility = View.GONE
+ if (contactsRefreshing) {
+ contactsRefreshing = false
+ binding.swipeRefresh.isRefreshing = false
+ }
}
}
diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt
index ed485a8..bf3b4e8 100644
--- a/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt
+++ b/app/src/main/java/sh/sar/basedbank/ui/home/DashboardFragment.kt
@@ -38,6 +38,11 @@ class DashboardFragment : Fragment() {
updateForeignLimits(viewModel.bmlLimits.value ?: emptyList())
}
+ binding.swipeRefresh.setOnRefreshListener {
+ (activity as? HomeActivity)?.triggerRefresh()
+ binding.swipeRefresh.isRefreshing = false
+ }
+
val bottomPaddingBase = (16 * resources.displayMetrics.density).toInt()
ViewCompat.setOnApplyWindowInsetsListener(binding.buttonBar) { v, insets ->
val isBottomNav = requireContext().getSharedPreferences("prefs", android.content.Context.MODE_PRIVATE).getBoolean("bottom_nav", false)
diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/FinancingFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/FinancingFragment.kt
index 5e8a8e0..7ab358d 100644
--- a/app/src/main/java/sh/sar/basedbank/ui/home/FinancingFragment.kt
+++ b/app/src/main/java/sh/sar/basedbank/ui/home/FinancingFragment.kt
@@ -18,6 +18,7 @@ class FinancingFragment : Fragment() {
private val binding get() = _binding!!
private val viewModel: HomeViewModel by activityViewModels()
private lateinit var adapter: FinancingAdapter
+ private var financingRefreshing = false
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = FragmentFinancingBinding.inflate(inflater, container, false)
@@ -38,11 +39,20 @@ class FinancingFragment : Fragment() {
insets
}
+ binding.swipeRefresh.setOnRefreshListener {
+ financingRefreshing = true
+ (activity as? HomeActivity)?.triggerRefreshFinancing()
+ }
+
viewModel.financing.observe(viewLifecycleOwner) { deals ->
adapter.updateDeals(deals)
binding.recyclerView.visibility = if (deals.isEmpty()) View.GONE else View.VISIBLE
binding.emptyView.visibility = if (deals.isEmpty()) View.VISIBLE else View.GONE
binding.loadingView.visibility = View.GONE
+ if (financingRefreshing) {
+ financingRefreshing = false
+ binding.swipeRefresh.isRefreshing = false
+ }
}
viewModel.hideAmounts.observe(viewLifecycleOwner) { adapter.setHideAmounts(it) }
}
diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/HomeActivity.kt b/app/src/main/java/sh/sar/basedbank/ui/home/HomeActivity.kt
index df61ab6..4a9863d 100644
--- a/app/src/main/java/sh/sar/basedbank/ui/home/HomeActivity.kt
+++ b/app/src/main/java/sh/sar/basedbank/ui/home/HomeActivity.kt
@@ -324,6 +324,18 @@ fun applyNavLabelVisibility() {
}
}
+ fun triggerRefresh() {
+ autoRefresh(CredentialStore(this))
+ }
+
+ fun triggerRefreshFinancing() {
+ val app = application as BasedBankApp
+ for ((loginId, session) in app.mibSessions) {
+ val profiles = app.mibProfilesMap[loginId] ?: emptyList()
+ refreshFinancing(loginId, session, profiles.filterVisibleProfiles(loginId))
+ }
+ }
+
fun setRefreshing(visible: Boolean) {
binding.refreshIndicator.visibility = if (visible) View.VISIBLE else View.GONE
}
@@ -337,11 +349,12 @@ fun applyNavLabelVisibility() {
override fun onResume() {
super.onResume()
- // Returning from LockActivity — skip the elapsed check and reset state.
+ // Returning from LockActivity — refresh sessions since they may have expired.
if (isLocked) {
isLocked = false
pauseTime = 0L
resetAutolockTimer()
+ autoRefresh(CredentialStore(this))
return
}
// If we were away long enough to have hit the autolock timeout (e.g. while
@@ -354,6 +367,9 @@ fun applyNavLabelVisibility() {
lock()
return
}
+ if (elapsed > 45_000L) {
+ autoRefresh(CredentialStore(this))
+ }
}
resetAutolockTimer()
}
diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/SettingsSecurityFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/SettingsSecurityFragment.kt
index fa25aa4..b61e3e4 100644
--- a/app/src/main/java/sh/sar/basedbank/ui/home/SettingsSecurityFragment.kt
+++ b/app/src/main/java/sh/sar/basedbank/ui/home/SettingsSecurityFragment.kt
@@ -60,7 +60,6 @@ class SettingsSecurityFragment : Fragment() {
// 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
@@ -69,7 +68,6 @@ class SettingsSecurityFragment : Fragment() {
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
diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt
index c855808..6ca7d64 100644
--- a/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt
+++ b/app/src/main/java/sh/sar/basedbank/ui/home/TransferFragment.kt
@@ -652,7 +652,7 @@ class TransferFragment : Fragment() {
ReceiptStore.save(requireContext(), receipt)
clearForm()
val activity = requireActivity() as HomeActivity
- activity.refreshBalances(src)
+ activity.triggerRefresh()
activity.showWithBackStack(TransferReceiptFragment.newInstance(receipt, capturedToAvatar))
} else if (!ok) {
Toast.makeText(requireContext(), msg, Toast.LENGTH_LONG).show()
diff --git a/app/src/main/java/sh/sar/basedbank/ui/home/TransferHistoryFragment.kt b/app/src/main/java/sh/sar/basedbank/ui/home/TransferHistoryFragment.kt
index f4958e1..31360fa 100644
--- a/app/src/main/java/sh/sar/basedbank/ui/home/TransferHistoryFragment.kt
+++ b/app/src/main/java/sh/sar/basedbank/ui/home/TransferHistoryFragment.kt
@@ -138,6 +138,14 @@ class TransferHistoryFragment : Fragment() {
}
(activity as? HomeActivity)?.setRefreshing(true)
loadNextPages()
+
+ binding.swipeRefresh.setOnRefreshListener {
+ if (isLoading) {
+ binding.swipeRefresh.isRefreshing = false
+ } else {
+ resetAndReload()
+ }
+ }
}
override fun onResume() {
@@ -145,6 +153,19 @@ class TransferHistoryFragment : Fragment() {
requireActivity().title = getString(R.string.nav_transfer_history)
}
+ private fun resetAndReload() {
+ allTransactions.clear()
+ pendingImageNames.clear()
+ pendingIconUrls.clear()
+ firstBatchDone = false
+ val accounts = accountStates.map { it.account }
+ accountStates.clear()
+ accounts.forEach { accountStates.add(AccountState(it)) }
+ adapter.setTransactions(emptyList())
+ binding.emptyView.visibility = View.GONE
+ loadNextPages()
+ }
+
private fun loadNextPages() {
val activeStates = accountStates.filter { it.hasMore() }
if (isLoading || activeStates.isEmpty()) return
@@ -250,6 +271,7 @@ class TransferHistoryFragment : Fragment() {
if (!firstBatchDone) {
firstBatchDone = true
(activity as? HomeActivity)?.setRefreshing(false)
+ binding.swipeRefresh.isRefreshing = false
}
if (newTransactions.isNotEmpty()) {
diff --git a/app/src/main/res/layout/fragment_account_history.xml b/app/src/main/res/layout/fragment_account_history.xml
index c486246..83fb1fc 100644
--- a/app/src/main/res/layout/fragment_account_history.xml
+++ b/app/src/main/res/layout/fragment_account_history.xml
@@ -31,28 +31,35 @@
-
-
+ android:layout_height="match_parent">
-
+
-
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_accounts.xml b/app/src/main/res/layout/fragment_accounts.xml
index eb483e6..5e10023 100644
--- a/app/src/main/res/layout/fragment_accounts.xml
+++ b/app/src/main/res/layout/fragment_accounts.xml
@@ -1,11 +1,18 @@
-
+ android:background="?attr/colorSurface">
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_contacts.xml b/app/src/main/res/layout/fragment_contacts.xml
index 47e29a9..5290113 100644
--- a/app/src/main/res/layout/fragment_contacts.xml
+++ b/app/src/main/res/layout/fragment_contacts.xml
@@ -41,34 +41,41 @@
app:tabMode="scrollable"
app:tabGravity="start" />
-
-
+ android:layout_height="match_parent">
-
+
-
+
-
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_dashboard.xml b/app/src/main/res/layout/fragment_dashboard.xml
index eb2cf94..5f40184 100644
--- a/app/src/main/res/layout/fragment_dashboard.xml
+++ b/app/src/main/res/layout/fragment_dashboard.xml
@@ -7,11 +7,16 @@
android:orientation="vertical"
android:background="?attr/colorSurface">
-
+
+
+
+
-
-
+ android:layout_height="match_parent">
-
+
-
+
-
+
-
+
-
+
+
+
+
+
diff --git a/app/src/main/res/layout/fragment_settings_security.xml b/app/src/main/res/layout/fragment_settings_security.xml
index f341a32..f87f9c1 100644
--- a/app/src/main/res/layout/fragment_settings_security.xml
+++ b/app/src/main/res/layout/fragment_settings_security.xml
@@ -112,14 +112,6 @@
app:singleSelection="true"
app:selectionRequired="true">
-
-
-
-
+ android:layout_height="match_parent">
-
+
-
+
+
+
+
+