Overview
Chasly is an offline-first, privacy-first personal finance tracker for Android — no account, no server, no analytics. It logs income and spending by category, shows a running balance with a monthly trend, and automates recurring transactions and savings goals, entirely on-device. Solo project, built in Kotlin and Jetpack Compose with a custom "buku kas" (ledger) design system.
Engineering highlights
Zero network permissions in the manifest — offline is an architectural decision, not a toggle.
Recurring transactions catch up on their own: a while-loop advances overdue occurrences, triggered by both a daily WorkManager job and ViewModel init, so a delayed background job never leaves the ledger behind.
Backup/restore runs on a versioned JSON schema with Replace vs. Merge (upsert-by-id) modes — the kind of migration pattern mature apps use, on a solo project.
CI handles non-code assets too: GitHub Actions downloads the three bundled fonts before every release build.
Tech stack
Language & UI
Data & persistence
Security
Build & release
Architecture
com/zaaam/chasly/ ├── ui/ # Compose screens — home, transaction, budget, recurring, lock ├── data/db/ # Room: 5 entities, 5 DAOs, v3 + 2 manual migrations ├── data/repository/FinanceRepository.kt # single source of truth ├── viewmodel/FinanceViewModel.kt # one ViewModel, StateFlow ├── worker/ # RecurringWorker, ReminderWorker └── utils/ # PinCrypto, Formatters, ReportExporter
DAO (Flow) → Repository (Flow + DataStore) → ViewModel (StateFlow) → Compose screen. MVVM, single module, no DI framework.
Features
- Running balance with a trend line, plus a monthly income vs. spending summary
- Custom categories — 13 seed defaults, 30-icon registry, full CRUD
- Recurring transactions (daily/weekly/monthly) with automatic catch-up
- Per-category budgets with 80%/over warnings, plus savings goals
- PIN + biometric lock, CSV/PDF export, local JSON backup & restore
Development process
Solo build, single-module MVVM with one Repository and one ViewModel — no DI framework. Shipped with a real README, an Apache-2.0 license, and a GitHub Actions workflow that handles the release APK end to end.
Challenges
Keeping the balance/trend view readable across very different amounts of data — a brand-new ledger and a six-month-old one need to look equally clear. Category renames also had to cascade through transactions, budgets, and recurring templates without corrupting old records.
What I learned
How to package a personal project properly — licensing, a real README, a repeatable release pipeline — instead of just shipping a debug APK.
