Mahari is a quiet luxury, offline-first Android financial intelligence application engineered to parse Safaricom M-Pesa SMS notifications, extract real transaction timestamps, structure cashflow, categorize spend, calculate daily budgets, track Fuliza overdrafts, and present monthly behavioral insights via on-device machine learning (XGBoost exported to ONNX).
Note
Handcrafted by Strathmore University students Okwudili Ujubuonu and Mark Gitau Irungu.
- Live Broadcast Receiver & Historical Backfill: Intercepts incoming notifications and scans past device inbox via SMS ContentProvider.
- 7 M-Pesa Format Patterns: Parses Paybill / Buy Goods, Person-to-Person Transfers, Agent Withdrawals/Deposits, Airtime/Data purchases, and Fuliza Overdraft allocations.
- Real SMS Date Extraction: Extracts exact transaction timestamps from message text (e.g.,
"on 26/7/26 at 2:34 PM") withTelephony.Sms.DATEmetadata fallback — never import time. - Full-Rescan Pull-to-Refresh: Pulling down on the dashboard triggers the full SMS backfill re-scan code path directly (
SmsBackfillManager.performOneTimeBackfill). - Retroactive One-Time Migration: Automatically re-parses and updates historical transactions' timestamps and running balances on startup.
- De-duplication: Code hash verification prevents duplicate entry creation.
- Standalone Dashboard Card: Displays current M-Pesa balance extracted from the most recent transaction with a valid balance reading.
- Nullable Balance Model:
runningBalanceis a nullableDouble?column, distinguishing genuine zero balances (Ksh 0.00) from unknown/absent balance readings (null). - Broadened Regex Support: Tollerates
Ksh,KES,Ksh.,KES., irregular spacing, negative overdraft balances (Ksh-350.00), and genuine zero values. - Fuliza & Parse-Failure Skipping: Transactions with absent or unparseable balances (such as standalone Fuliza overdraft alerts) are transparently skipped in balance selection queries.
- Current Month Default: Defaults to current calendar month (e.g. July 2026) on fresh launch. Past/custom selections do not persist across app sessions.
- Month Stepper (
< July 2026 >): Fast month navigation arrows capped at the current real-world month. - Custom Range Picker: 📆 Calendar picker for custom start & end date ranges with a one-tap "Back to Current Month" snap-back button.
- Search All-Time Override: 🌐 Search bar respects current date-scope by default, with an explicit "Search All Time" toggle chip.
- Recent Transactions Top 5 Preview: Dashboard displays a compact card showing only the 5 most recent transactions (slim rows with merchant, category icon, amount, and time), with a "See all {N} transactions" button that opens
FullTransactionListSheetmodal sheet. Automatically hides the button when <= 5 items exist.
- OLED Warm Charcoal Background:
#16181A(WarmCharcoalBgDark) for warm, rich OLED dark mode. - Warm Metal Accents: Antique Brass (
#B08D57Dark /#8C6D3FLight) for card borders, thin dividers, and structural accents. - Precious Hero Emerald Restraint:
#34D399Dark /#059669Light reserved strictly for the single hero budget figure per screen. - Eased Motion: Cubic bezier transitions (
FastOutSlowInEasing) for progress fills and count-up figures. - Premium Biometric Lock Screen: Custom pre-auth screen with centered Mahari cowrie mark inside a Warm Metal ring.
- Renders custom Mahari share cards with theme-matching background, Warm Metal borders, and cowrie branding mark (
🐚 MAHARI). - Balance Masking: Includes a toggle to mask sensitive balances (
Ksh ••••••) before sharing. - FLAG_SECURE Compliance:
FLAG_SECUREremains 100% active to block OS-level screenshots while supporting in-app image export via Android Share Sheet (FileProvider).
- 100% Offline-First Default: Cloud sync is off by default and requires explicit user consent via confirmation modal.
- Data Minimization Guarantee: Raw SMS text, phone numbers, and contact names never leave the device. Only structured DTOs (
amount,category,merchant,timestamp,isExpense) are uploaded over HTTPS/TLS. - Python FastAPI Backend on Render Free Tier (
mahari_backend/): Deployed viarender.yamlto Render's free tier. Runs XGBoost + SHAP feature attribution engine and encrypts data at rest usingcryptography.fernet.Fernet. - Free-Tier Deployment Notice: Render's free tier spins down after ~15 minutes of inactivity. Cold starts may take a few seconds to wake on subsequent requests. If cloud traffic exceeds free-tier limits, infrastructure needs revisiting.
- Permanent Data Deletion: Includes
DELETE /api/v1/user-data/{deviceId}endpoint to permanently purge all synced cloud data.
- Android 13+ Runtime Permission: Explicitly requests
POST_NOTIFICATIONSruntime permission during onboarding and app startup to guarantee push alerts are delivered. - Real-Time Transaction Alerts: Fires instant
"💸 Payment Sent"(for outgoing expenses) and"💰 Money Received"(for incoming payments) notifications when M-Pesa SMS messages arrive. - Foreground Auto-Refresh: Automatically re-scans inbox on app cold launch and return from background, guarded by a 60-second throttle interval to conserve battery and storage IO.
- Dynamic Keyword Matrix: Auto-categorizes merchants, dining, supermarkets, fuel, and transport.
- Learning Memory: Remembers user recategorizations in a local Room database (
merchant_category_mappings) and applies them to future transactions from that merchant.
To prevent transaction visibility regressions (e.g. transactions disappearing after schema changes or date parsing adjustments), Mahari enforces strict automated and runtime guardrails:
- Automated Regression Suite (
TransactionVisibilityRegressionTest.kt):- Seeds transactions across distinct months, balances (
positive,0.0, andnull), and format variations. - Runs database migrations (
MIGRATION_4_5) and asserts that total record count and query visibility are preserved 100%.
- Seeds transactions across distinct months, balances (
- Runtime Sanity Check (
DATA_INTEGRITY_SANITY_CHECK):- On every dashboard calculation pass, the ViewModel compares total raw SQLite database transactions against currently visible scoped transactions.
- Emits a high-priority warning log (
⚠️ SANITY WARNING: Database contains N transactions, but current scope returned 0) if date filters conceal all records.
- Mandatory Developer Checklist:
- Any commit modifying
TransactionEntity, Room schema versions, orDateScopeModemust executeTransactionVisibilityRegressionTestand verify raw DB count equality before release.
- Any commit modifying
Mahari's visual identity balances financial clarity with luxury restraint:
| Token Role | Color (Dark Mode) | Color (Light Mode) | Usage |
|---|---|---|---|
| Background | #16181A (Warm Charcoal) |
#F4F3EF (Warm Bone) |
OLED near-black warm canvas |
| Card Surface | #1F2226 |
#FFFFFF |
Primary card containers |
| Warm Metal Accent | #B08D57 (Antique Brass) |
#8C6D3F (Soft Copper) |
Card borders, dividers, structural lines |
| Hero Emerald | #34D399 |
#059669 |
Reserved strictly for hero numbers |
- JDK 21 (bundled JetBrains Runtime
jbror OpenJDK 21) - Android SDK 35 & Android CLI (
adb) - Gradle 8.11.1
-
Clone Repository:
git clone https://github.com/dilithegit/Mahari.git cd Mahari -
Run Unit Tests:
./gradlew testDebugUnitTest
-
Build & Install Debug APK:
./gradlew assembleDebug adb install -r app/build/outputs/apk/debug/app-debug.apk
Handcrafted with precision by Strathmore University students:
- Okwudili Ujubuonu
- Mark Gitau Irungu
Distributed under the MIT License. See LICENSE for details.