Commit Graph
18 Commits
Author SHA1 Message Date
ngenohkevin 4e58bd8a8b v1.4.1: own-account transfers + Fuliza limit tracking
Marking a recipient as 'my own account' reclassifies its transactions
into a new transfer category excluded from all spending figures —
bank/card top-ups are moved money, not spending.

Fuliza coverage: accept the dedicated FULIZA sender id in the receiver
and both importers (borrows were going untracked while repayments were
captured), capture the available Fuliza limit from any SMS carrying it,
record borrow interest as a fee, and surface an outstanding/available
card on Home.

DB v3->v4: additive ALTER TABLEs only (categories.isTransfer,
transactions.fulizaLimit) + idempotent seed of the My Accounts category.
2026-06-07 15:57:40 +03:00
ngenohkevin 9600fa730c v1.4.0: true spending totals + monthly trends
Spending figures were inflated: Fuliza repayments double-counted the
already-counted borrow-backed purchase, M-Shwari/KCB deposits counted
own-money savings as spending, and reversed transactions never came
back out of the total. All spending queries now exclude these.

Also adds a month-by-month breakdown card to Trends (12 months,
delta vs previous month) and captures the credited amount on
standard-format reversal SMS instead of recording 0.0.
2026-06-07 15:24:59 +03:00
ngenohkevin f40a93e62a v1.3.6: auto-dismiss stale Fuliza insight + refresh after reparse
The Fuliza outstanding insight is stateful — it should disappear the
moment the underlying outstanding hits 0. Today it sticks until
manually dismissed, even after data updates.

InsightEngine now tracks a STATEFUL_KEYS set (just "fuliza:outstanding"
for now). If a rule that owns one of those keys returns no candidates
in this run, the row is deleted outright rather than being left around
as a tombstone.

SettingsViewModel.reparseAll() now also calls insightsRepository
.generateAll() once the row update completes. So the moment the user
finishes "Re-parse ALL transactions", any stale Fuliza alert reading
the limit-as-outstanding goes away, and any newly correct alerts show
up immediately.
2026-05-16 22:30:17 +03:00
ngenohkevin f83f644cd3 v1.3.5: reparse progress spinner + Today default on Home
- 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.
2026-05-16 22:08:59 +03:00
ngenohkevin 6571cd0497 v1.3.4: fix Fuliza auto-pay balance + outstanding + reparse-all
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.
2026-05-16 21:56:10 +03:00
ngenohkevin c5be379c8e v1.3.3: donut chart circularity + period selector on Home
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").
2026-05-16 21:28:37 +03:00
ngenohkevin 5ca2f89303 v1.3.2 hotfix: migration FK crash + dark-theme insight teaser
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.
2026-05-16 13:55:31 +03:00
ngenohkevin 3dc9e0305e v1.3.1 hotfix: status-bar overlap + font fallback
- 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.
2026-05-16 13:36:23 +03:00
ngenohkevin 85c3f167ab Untrack internal planning docs
LEDGA_REDESIGN.md and PROJECT_PLAN.md are kept locally for reference
but shouldn't live in the public repo.
2026-05-16 13:26:22 +03:00
ngenohkevin 51264301a8 v1.3.0: major redesign — bento UI, Insights, Goals, multi-SIM, in-app updates
UI overhaul
- Modern fintech bento design system (LedgaText, Space, Radius, Motion).
- Inter font via Google Fonts.
- New PillTabBar + LedgaTopBar; 4-tab IA: Home / Activity / Insights / You.
- Home rewritten as bento cards; new AccountChip leading slot.
- Activity wraps Transactions + Trends behind a segmented control.
- Trends gains a GitHub-style heatmap calendar.
- You consolidates Settings into grouped bento cards.

Insights (Phase C)
- Rule engine + 6 rules: anomaly (z-score), recurring detection, fee tips,
  Fuliza outstanding, Fuliza auto-pay pairing, positive nudges.
- Daily InsightWorker (3 AM); generates on screen entry for first-time users.
- Natural-key dedup so dismiss/snooze state survives re-runs.
- Home teaser surfaces the top active insight.

Goals (Phase D)
- New Goal entity + GoalContribution table.
- 3 contribution rules: AllSavingsDeposits, ToRecipient, Manual.
- Rule-based contributions compute live via SQL aggregation (no
  duplicate rows). Manual rule uses goal_contributions table.
- GoalsScreen with progress rings + status badges; AddGoalSheet;
  GoalDetailScreen with contributing-transactions list.
- "Add to goal" entry in TransactionDetailSheet for Manual goals.

Multi-SIM (Phase E)
- MpesaAccount entity captured per-subscription on SMS receive
  (SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX with legacy fallback).
- READ_PHONE_STATE permission (optional; falls back to "Line N" labels).
- Every screen-facing query is account-scoped via TransactionRepository
  composing with the selected-account flow.
- AccountSwitcherSheet on Home; AccountsScreen with rename / recolor /
  set-primary; BackfillScreen with 3-prong attribution (SMS-inbox auto,
  date range, per-transaction manual via TransactionDetailSheet).

In-app updates (Phase F + G)
- Chunked APK download with progress reporting.
- UpdateRepository state machine (Idle → Downloading → Ready → Failed).
- Full-screen UpdateScreen with parsed What's new / Fixes sections.
- "Remind me later" persists dismissed version.
- Background UpdateCheckWorker pre-fetches APK on unmetered network.
- Home banner: "available" vs "ready to install" copy variants.
- Proactive REQUEST_INSTALL_PACKAGES permission prompt with settings
  deep-link before firing the installer intent.

Parser quality
- Hustler Fund "sent KshX to NAME" amount-first phrasing now matches.
- "Give cash to AGENT" agent-deposit format recognized as DEPOSIT.
- Fuliza auto-pay recipient labels rewritten as "Fuliza overdraft
  (auto-cleared / auto-partial)" for clearer transaction lists.
- 8 new real-world regression fixtures from sanitized export.
- Audit: 0 UNKNOWN against 6,565-transaction real history (was 838).

Database
- v3 schema: mpesa_accounts, goals, goal_contributions, insights tables.
- transactions gains accountId (FK), note.
- Explicit MIGRATION_1_2 + MIGRATION_2_3.

Tests
- 65 unit tests across parser (33), rule engine (14), goal rules (8),
  update logic (8), parser audit (1), insights dry-run (1).
- Two test suites (audit, dry-run) read the local export when present,
  skip cleanly in CI.

Design spec
- LEDGA_REDESIGN.md captures the full direction + tokens + per-screen
  wireframes + phased PR plan.

versionCode 3, versionName 1.3.0.
2026-05-16 13:17:54 +03:00
ngenohkevin 00975aa7cc Fix update notification: add banner UI + reset rate limit on version change 2026-03-21 22:35:00 +03:00
ngenohkevin d29498edbb Add re-parse unparsed messages button
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.
2026-03-21 22:26:45 +03:00
ngenohkevin 729ef15cd3 Fix parser + redesign UI
Parser fixes (838 → ~0 unparsed):
- Fuliza auto-deduction (full/partial pay from balance)
- Withdraw alt format (Withdraw Ksh from agent)
- Reversal alt format (Reversal of transaction)
- Airtime self with lowercase confirmed
- Hustler Fund with 4-digit year dates
- Balance check messages filtered out
- SMS normalization (newlines, extra spaces)
- Amount regex handles Ksh with/without space

UI improvements:
- Category-specific Material icons (not generic cart)
- Colored icon backgrounds with rounded corners
- Green balance card with M-Pesa branding
- Category breakdown section (where your money went)
- Balance uses last non-zero transaction
- Better card shapes (20dp rounded)
- Font weight hierarchy
2026-03-21 22:11:36 +03:00
ngenohkevin 0f2ad432d6 Fix CI: add contents write permission for release creation 2026-03-21 21:35:06 +03:00
ngenohkevin e7120fe5ee Fix: remove default WorkManagerInitializer for Hilt custom init 2026-03-21 21:24:11 +03:00
ngenohkevin 706993e084 Fix CI: create keystore directory before decoding 2026-03-21 21:12:09 +03:00
ngenohkevin 047565500e Add README with features, tech stack, and usage docs 2026-03-21 21:11:26 +03:00
ngenohkevin 79741192b9 Initial release — Ledga M-Pesa spending tracker
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
2026-03-21 20:53:59 +03:00