Open-source, privacy-first expense tracker for Android. It reads the transaction SMS your bank already sends you, turns them into a spending history, and learns your categories over time. No account, no cloud, no internet permission — your data never leaves the phone.
Status: early testing. Not on the Play Store. Debug-signed APKs only. Built for the author and a small circle of testers — expect rough edges, and please report them.
Most expense trackers want you to type in every purchase, or to link your bank account. TRACKO does neither. Indian banks already SMS you for every UPI payment, card swipe and transfer. That inbox is your transaction history — it just needs reading.
So TRACKO reads it. On your phone. With no server anywhere in the picture.
Automatic capture
- Parses transaction SMS on arrival (instant) and on a periodic safety-net scan (every 6h) — plus a silent re-check whenever you open the app
- Extracts amount, payee, UPI/bank reference, and payment method
- Duplicate-proof: bank + app double-SMS and repeated scans never double-count
- Recognises senders from major Indian banks and UPI apps (HDFC, SBI, ICICI, Axis, Canara, Federal, HSBC, GPay, PhonePe, Paytm, BHIM, Amazon Pay and more); the generic parser handles formats it has never seen
- Filters out noise: balance alerts, OTPs and promos are not transactions
Categorization without AI
- Built-in rules — ~100 known merchant keywords (Zomato → Food, Uber → Transport…) work from day one
- Your history — categorize a payee once and it's remembered forever; next time it's tagged silently
- Ask you — genuinely new payees raise a notification with one-tap category buttons
No model, no cloud call, no black box. Every decision is traceable to a rule or a tap you made.
Money in and money out
- Income and expenses tracked separately — income never gets counted as spending
- Dashboard: period spend, received, transaction count, uncategorized backlog
- Transactions: filters (all / uncategorized / categorized), search, long-press bulk select and categorize, delete
- Stats: month-over-month verdict, daily spending bars you can tap for per-day detail, category breakdown with drill-down, top merchant and payment method
- CSV export of the current month — your data stays yours and portable
Getting started is guided A first-run wizard walks you through the two things Android makes awkward (Play Protect and restricted settings) and requests SMS + notification access inside the app.
This is the part that matters, so here are verified facts — not marketing:
| Question | Answer |
|---|---|
| Where does the data live? | On your phone only, in app-private storage at /data/data/com.expensetracker/databases/. |
| Does anything leave the device? | No. The app declares no internet permission and contains zero networking code — no HTTP client, analytics, ads, or crash-reporting SDK. |
| Is there an account? | No. No sign-up, no email, no user ID. |
| Is categorization AI? | No. Deterministic regex + rules + your own taps. Nothing is inferred in the cloud. |
| Is my SMS stored? | Matching transaction SMS text is kept locally as a reference field on each transaction. Non-transaction SMS are ignored. |
| Cloud backup? | The database is excluded from Android auto-backup and device transfer (backup_rules.xml, data_extraction_rules.xml). |
| Permissions requested | READ_SMS (core function), POST_NOTIFICATIONS (alerts), WAKE_LOCK (background scan). Nothing else. |
| Encryption at rest? | Not yet. Plaintext SQLite. Rooted-device risk. SQLCipher is on the roadmap. |
Audit it yourself: grep -ri "internet\|http\|okhttp\|retrofit\|analytics" app/src/main returns nothing relevant by design.
- Download the latest APK from Releases.
- Play Protect will warn you. That is expected: a debug-signed, sideloaded app that reads SMS looks exactly like SMS-stealing malware. The app holds no internet permission, which is the strongest counter-evidence available — check the manifest. Pause Play Protect to install.
- On Android 13+, SMS permission needs one extra step. Sideloaded apps are quarantined from restricted permissions. Go to App info → ⋮ → Allow restricted settings, then grant SMS. The in-app wizard walks you through it.
- Open the app — your history imports automatically.
Release signing and a Play Store listing are planned but not done. Until then, these prompts are unavoidable.
SMS arrives (or inbox scan)
→ is it a transaction? keywords + currency + known sender, balance alerts excluded
→ parse amount, payee, method, UPI ref, direction
→ duplicate check amount + payee + 5-minute window
→ auto-categorize your memory → built-in rules → ask you
→ store locally Room / SQLite
Three ingestion paths (instant receiver, 6-hourly worker, on-open import) all funnel through the same deduplication, so overlap is harmless and nothing is counted twice.
Full technical deep-dive — schema, migrations, parsers, decision log, roadmap — lives in docs/IMPLEMENTATION.md. A non-technical overview is in docs/PROJECT_OVERVIEW.md.
./gradlew assembleDebug # debug APK → app/build/outputs/apk/debug/
./gradlew testDebugUnitTest # JVM unit testsRequires JDK 17 and the Android SDK. No API keys, no secrets, no local.properties needed — there is nothing for the app to authenticate against.
| minSdk / targetSdk | 24 (Android 7.0) / 34 |
| Language | Kotlin 1.9.20 |
| UI | Jetpack Compose + Material 3 |
| DI | Hilt |
| Database | Room |
| Background | WorkManager, BroadcastReceiver |
| Tests | 242 unit tests, green on CI |
The domain/ layer is deliberately free of Android imports so the parsing and categorization logic stays testable on a plain JVM. Please keep it that way.
Issues and PRs are welcome. Ground rules:
- CI must be green (
assemble+unit-tests). - New logic in
domain/should come with JVM unit tests. - Don't add networking, analytics, or crash-reporting dependencies — they break the app's core guarantee.
- Bigger changes: open an issue first so we can agree on the approach before you write code.
Known gaps if you're looking for somewhere to start: manual transactions (payments with no SMS), one-category-per-payee ambiguity, budgets, at-rest encryption, release signing.
- SMS-only. A payment that sends no SMS is invisible. Some small UPI debits, wallet spends and refunds are never messaged.
- One category per payee. Pay the same person for rent and lunch and it can only remember one.
- No manual entry yet — you can't add cash spending.
- No budgets or spending limits yet.
- Debug-signed, not on the Play Store.
- Not encrypted at rest.
Built with Android Jetpack (Compose, Room, WorkManager), Hilt, MockK and kotlinx-coroutines-test. Charts are hand-written Compose. No analytics, ad, or networking SDK is included — that is a hard rule for this project, not a coincidence.
MIT — do what you like, just keep the notice.
Copyright (c) 2026 Surya