- Re-parse ALL now shows a spinner + "Re-parsing all transactions…"
while running, so taps don't feel ignored. Button is hidden during
the work; reappears after the result line.
- Empty result (no transactions to re-parse) shows a hint pointing at
"Import SMS history" instead of a confusing "0 of 0".
- Home period selector defaults to Today instead of Month — the spec
showed Month first, but in practice users want to see today's
spending at a glance and switch out only when needed.
Two parser bugs around Fuliza auto-pay that produced wrong balance and
phantom-outstanding alerts:
1. BALANCE_REGEX required the literal "Ksh" before the amount, but the
full-clear auto-pay SMS ends "Your M-PESA balance is 8581.08." with
no Ksh prefix. Balance was lost → stored as 0 → Home's
getLatestTransactionWithBalance filter (balance > 0) fell back to a
stale older transaction. Made the Ksh optional.
2. parseFulizaAutoPay was stuffing the *available Fuliza limit* into
fulizaOutstanding. Those are opposites (available = ceiling - owed).
After a full clear that meant FulizaRule saw the full 14k limit and
raised "You owe Ksh 14,000" — incorrect. Now:
- full clear -> fulizaOutstanding = 0 (rule correctly bails)
- partial -> fulizaOutstanding = null (rule defers to the
previous Fuliza tx;
avoids reporting the
available-limit value)
Reparse all transactions
- New "Re-parse ALL transactions" entry in You → Data. Walks every
stored row through MpesaSmsParser and updates parser-derived fields
in place, preserving user fields (categoryId, accountId, note, id).
- Needed because the broken regex above silently corrupted historical
rows on import; this is the no-data-loss path back to correctness.
DonutChart was hardcoded to a 180dp Canvas. When placed in a 50/50 Row
inside the "Where it went" card on the S24 Ultra, the canvas overflowed
the parent column and clipped into an oval. Switched to
fillMaxWidth + aspectRatio(1f) + fillMaxSize Canvas so the donut always
stays circular and scales to whatever the parent gives it. Stroke width
also scales (18% of the box, capped at 32dp).
Home screen lost the Today / This Week / Month period selector in the
bento rewrite (HomeViewModel still exposed it, the UI just never
rendered it). Restored as a LedgaChip row above the hero card. The
hero overline now also tracks the selection ("SPENT TODAY" /
"SPENT THIS WEEK" / "SPENT THIS MONTH").
MIGRATION_1_2 was using ALTER TABLE to add transactions.accountId, but
SQLite can't add a FOREIGN KEY via ALTER. Room validates schema after
migration and threw IllegalStateException ("Migration didn't properly
handle: transactions") because the FK to mpesa_accounts was missing.
The whole DB stayed at v1, blocking every read/write — which is why
SMS import + SMS receive looked dead too.
Fix: standard SQLite table-swap dance. Create transactions_new with
both FKs declared, INSERT … SELECT old rows in, drop the old table,
rename. All indices recreated. Defensive DROP IF EXISTS in case a
prior failed run left junk.
BentoCard now picks title + overline colors from the tonal background
luminance instead of unconditionally using onSurface. On dark theme
the Insights teaser had near-white text on the (light-theme) pale
green bg — invisible. Now it adapts:
- light tonal bg → dark ink
- dark tonal bg → light ink
HomeScreen Insights teaser switched from hardcoded LedgaAccentSoft to
MaterialTheme.colorScheme.primaryContainer so the bg itself also
adapts to theme.
- LedgaTopBar now applies WindowInsets.statusBars padding. Previously
enableEdgeToEdge was on but the top bar drew at y=0, so the avatar
and trailing icons sat under the system clock / WiFi indicator.
- PillTabBar uses WindowInsets.navigationBars padding instead of a
hardcoded 21dp bottom so the floating pill sits above the system
gesture pill on devices like the S24 Ultra.
- Typography switched from Google-Fonts Inter to FontFamily.SansSerif.
Inter requires a downloadable-fonts round-trip via Google Play
Services that doesn't always succeed; the fallback was hitting
FontFamily.Default, which inherits the user's system font choice —
on Samsung One UI that's frequently a handwritten / script style
and broke the type rhythm everywhere. SansSerif uses the platform
sans (One UI Sans on Galaxy, Roboto elsewhere) regardless of user
preference.
Settings > Data now has "Re-parse Unparsed Messages" button that
re-runs the fixed parser on all UNKNOWN transactions, properly
categorizing previously missed Fuliza auto-deductions, withdrawals,
airtime, and reversals.
Complete Android app (Kotlin/Jetpack Compose) with:
- SMS parser for all 17 M-Pesa transaction types
- Room database with auto-categorization engine
- Real-time SMS capture via BroadcastReceiver
- Onboarding flow with SMS history import
- Home dashboard with balance, spending, donut chart
- Transactions list with search, filters, day grouping
- Trends screen with Vico bar charts, top merchants
- Budget system with progress tracking
- Transaction detail sheet with re-categorization
- Notifications (daily/weekly summaries, budget alerts)
- Google Drive backup & restore
- Manual export/import (CSV + JSON ZIP)
- Self-update system via GitHub Releases API
- Theme system (System/Light/Dark) + font scaling
- Adaptive app icon with splash screen
- GitHub Actions CI/CD for release APK builds