From 045eb1b1a2ad63e420d37972d01d9d67e0aab5a2 Mon Sep 17 00:00:00 2001 From: ahusan Date: Thu, 28 May 2026 18:02:50 +0500 Subject: [PATCH] =?UTF-8?q?ui:=20pass=203=20=E2=80=94=20skeleton=20loading?= =?UTF-8?q?,=20a11y,=20snackbars,=20dark=20mode?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skeleton loading state: * Wire item_skeleton_transaction (previously dormant in res) into AccountHistoryFragment and TransferHistoryFragment. When a load starts with no cached transactions, 8 skeleton rows appear under the search bar instead of a blank screen. Skeleton hides as soon as transactions arrive or the load completes. * Decision: ActivitiesFragment loads from local storage synchronously, so no skeleton there — would only flash for a frame. Accessibility: * Decorative icons in item_more_nav, item_nav_slot, item_picker_row, and item_account_dropdown now declare importantForAccessibility="no" so TalkBack skips them (the adjacent text label conveys meaning). * Also added tnum to item_picker_row.tvBalance and item_account_dropdown.tvDropdownBalance — missed amounts from Pass 2. Snackbar: * ContactsFragment.deleteContact's success/failure feedback is now a Snackbar instead of Toast — the fragment stays visible, so a bottom-anchored Snackbar fits better than a floating Toast. * Other Toasts are either followed by navigation away (Snackbar couldn't display), or purely informational ("copied", "press back to exit") where Toast is the right primitive. Left as-is. Dark mode: * Connectivity banner in activity_home no longer hardcodes #C62828 / #FFFFFF — uses colorErrorContainer / colorOnErrorContainer so it adapts to the user's theme. * Other hex literals (card-artwork text, receipt screens that intentionally emulate paper) are intentional and stay. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../basedbank/ui/home/AccountHistoryFragment.kt | 6 ++++++ .../sh/sar/basedbank/ui/home/ContactsFragment.kt | 5 +++-- .../basedbank/ui/home/TransferHistoryFragment.kt | 6 ++++++ app/src/main/res/layout/activity_home.xml | 4 ++-- .../main/res/layout/fragment_account_history.xml | 16 ++++++++++++++++ .../res/layout/fragment_transfer_history.xml | 16 ++++++++++++++++ .../main/res/layout/item_account_dropdown.xml | 4 +++- app/src/main/res/layout/item_more_nav.xml | 3 ++- app/src/main/res/layout/item_nav_slot.xml | 3 ++- app/src/main/res/layout/item_picker_row.xml | 2 ++ 10 files changed, 58 insertions(+), 7 deletions(-) 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 2d4232d..64ed377 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 @@ -142,6 +142,8 @@ class AccountHistoryFragment : Fragment() { } adapter.setTransactions(filtered) binding.emptyView.visibility = if (filtered.isEmpty() && !isLoading) View.VISIBLE else View.GONE + binding.skeletonContainer.visibility = + if (allTransactions.isEmpty() && isLoading) View.VISIBLE else View.GONE } private fun resetAndReload() { @@ -168,6 +170,9 @@ class AccountHistoryFragment : Fragment() { if (firstPageDone && allTransactions.isNotEmpty()) { adapter.showLoadingFooter = true + } else if (allTransactions.isEmpty()) { + binding.skeletonContainer.visibility = View.VISIBLE + binding.emptyView.visibility = View.GONE } val app = requireActivity().application as BasedBankApp @@ -187,6 +192,7 @@ class AccountHistoryFragment : Fragment() { isLoading = false if (_binding == null) return@launch + binding.skeletonContainer.visibility = View.GONE if (!firstPageDone) { firstPageDone = true 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 fb0897a..e013690 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 @@ -9,6 +9,7 @@ import android.view.View import android.view.ViewGroup import android.widget.Toast import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.google.android.material.snackbar.Snackbar import androidx.core.view.ViewCompat import androidx.core.view.WindowInsetsCompat import androidx.core.widget.addTextChangedListener @@ -194,10 +195,10 @@ class ContactsFragment : Fragment() { viewLifecycleOwner.lifecycleScope.launch { val success = withContext(Dispatchers.IO) { ContactManager.delete(contact, app) } if (success) { - Toast.makeText(requireContext(), R.string.contact_deleted, Toast.LENGTH_SHORT).show() removeFromViewModel(contact) + Snackbar.make(binding.root, R.string.contact_deleted, Snackbar.LENGTH_SHORT).show() } else { - Toast.makeText(requireContext(), R.string.contact_delete_failed, Toast.LENGTH_SHORT).show() + Snackbar.make(binding.root, R.string.contact_delete_failed, Snackbar.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 82bac28..0a8d9f1 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 @@ -181,6 +181,9 @@ class TransferHistoryFragment : Fragment() { if (firstBatchDone && allTransactions.isNotEmpty()) { adapter.showLoadingFooter = true + } else if (allTransactions.isEmpty()) { + binding.skeletonContainer.visibility = View.VISIBLE + binding.emptyView.visibility = View.GONE } val app = requireActivity().application as BasedBankApp @@ -286,6 +289,7 @@ class TransferHistoryFragment : Fragment() { isLoading = false if (_binding == null) return@launch + binding.skeletonContainer.visibility = View.GONE val raw = bannerMsg.get() when { @@ -329,6 +333,8 @@ class TransferHistoryFragment : Fragment() { } adapter.setTransactions(filtered) binding.emptyView.visibility = if (filtered.isEmpty() && !isLoading) View.VISIBLE else View.GONE + binding.skeletonContainer.visibility = + if (allTransactions.isEmpty() && isLoading) View.VISIBLE else View.GONE } private fun loadContactImage(name: String) { diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 3b76046..dbbb6b3 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -44,8 +44,8 @@ android:id="@+id/connectivityBanner" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="#C62828" - android:textColor="#FFFFFF" + android:background="?attr/colorErrorContainer" + android:textColor="?attr/colorOnErrorContainer" android:gravity="center" android:paddingTop="6dp" android:paddingBottom="6dp" diff --git a/app/src/main/res/layout/fragment_account_history.xml b/app/src/main/res/layout/fragment_account_history.xml index 6e823cf..c368fa3 100644 --- a/app/src/main/res/layout/fragment_account_history.xml +++ b/app/src/main/res/layout/fragment_account_history.xml @@ -48,6 +48,22 @@ android:paddingBottom="16dp" android:clipToPadding="false" /> + + + + + + + + + + + + + + + + + + + + + + @@ -49,7 +50,8 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?attr/textAppearanceBodySmall" - android:textColor="?attr/colorOnSurface" /> + android:textColor="?attr/colorOnSurface" + android:fontFeatureSettings="tnum" /> diff --git a/app/src/main/res/layout/item_more_nav.xml b/app/src/main/res/layout/item_more_nav.xml index 3d9db65..cc19c1c 100644 --- a/app/src/main/res/layout/item_more_nav.xml +++ b/app/src/main/res/layout/item_more_nav.xml @@ -13,7 +13,8 @@ android:id="@+id/ivIcon" android:layout_width="24dp" android:layout_height="24dp" - android:layout_marginEnd="16dp" /> + android:layout_marginEnd="16dp" + android:importantForAccessibility="no" /> + android:layout_marginBottom="6dp" + android:importantForAccessibility="no" />