expense-tracking is a strictly offline Android expense tracker written in Kotlin. Its purpose is to remove the manual bookkeeping burden from everyday Indian UPI spending: instead of typing every payment into a spreadsheet, the user lets the app read the UPI debit and credit SMS messages their bank already sends, parse the amount, date and merchant out of them, and file each one as a ledger row in a local Room database. Manual create, read, update and delete operations exist alongside the automatic capture so the record can be corrected and completed, and interactive graph visualizations turn the accumulated ledger into spending insight.
The audience is a pragmatic Indian Android user who pays by UPI, wants an accurate record of where money went, and is deliberately unwilling to hand that record to a cloud service. There is no login, no cloud sync, no Firebase and no internet permission of any kind — the ledger lives on the device and nowhere else. The interface inherits the modern, component-based "AI-Enabled Finance Tracking" design system and is rendered as a calm, instrument-panel-like ledger rather than a marketing surface.
This document is the System Requirements Document for the product; it specifies the app's features, data model, SMS parsing engine, architecture separation and delivery boundaries in full.
expense-tracking is a single-user, on-device Android application. Its two entry paths converge on one local ledger:
READ_SMS and RECEIVE_SMS permissions, and hands UPI debit/credit message bodies to a parser that extracts amount, date and merchant name for standard Indian bank formats (SBI, HDFC, ICICI). Parsed results are written through the Repository into the Room database as expense (debit) or income (credit) entries tagged as SMS-sourced.Architecture is strict MVVM with Clean Architecture layering: the Broadcast Receiver is the trigger only, the Repository owns all data access, and the UI (lists and graphs) observes state through ViewModels. Nothing in the app performs network I/O.
The only active human actor is the Expense Tracker (App User) — the person who owns the device and its financial record. There are no other human roles, no administrators, no shared accounts and no counterparties inside the product.
Removed from scope
The product is delivered as an Android application installed on the user's own device. All durable state — every expense and income entry, whether captured from SMS or entered manually — is held in a local Room database on that device. The app requests READ_SMS and RECEIVE_SMS so the SMS trigger can function; if those permissions are not granted, automatic capture is unavailable and the user continues with manual entry only. There is no identity layer: the app opens directly into its own content, because there is no login and no account to establish. Access to every destination is therefore unauthenticated and local.
The delivery boundary is deliberately narrow. Nothing leaves the device, nothing is fetched from a server, and no remote party participates in any workflow. The provisioned shared backend, database and frontend services in the project inventory are infrastructure scaffolding and are not part of this product's runtime behavior; the product's runtime is the Android app and its on-device Room store. The current horizon contains only the SMS-driven ledger, manual CRUD and graph insights described here; no future capabilities are committed.
READ_SMS/RECEIVE_SMS permissions when not yet granted; jump to Add Entry to record an expense manually.FR-1 — Offline Android app in Kotlin. As an Expense Tracker (App User), I should have an Android expense tracking app built in Kotlin that runs entirely on my device, so that my financial record never depends on a network. (explicit)
FR-2 — Automatic entry from UPI SMS. As an Expense Tracker (App User), I should have expense and income entries created automatically from the Indian UPI debit/credit SMS messages my bank sends, so that I do not have to type every payment. (explicit)
FR-3 — SMS permissions. As an Expense Tracker (App User), I should be asked for READ_SMS and RECEIVE_SMS permissions so that incoming UPI SMS can be captured and parsed. (explicit)
FR-4 — Manual create. As an Expense Tracker (App User), I should be able to manually create an expense entry so that spending not captured by SMS is still recorded. (explicit)
FR-5 — Manual read. As an Expense Tracker (App User), I should be able to read my recorded entries, both SMS-captured and manual, so that I can review what I have spent and received. (explicit)
FR-6 — Manual update. As an Expense Tracker (App User), I should be able to update an existing expense entry so that incorrect or incomplete captured data can be corrected. (explicit)
FR-7 — Manual delete. As an Expense Tracker (App User), I should be able to delete an expense entry so that erroneous or unwanted records can be removed. (explicit)
FR-8 — Interactive graph visualizations. As an Expense Tracker (App User), I should be able to view interactive graph visualizations of my expenses so that I can understand where my money goes. (explicit)
FR-9 — Local Room storage. As an Expense Tracker (App User), I should have all entries stored in a local Room database on my device so that my record is durable and private. (explicit)
FR-10 — Design-system-conformant UI. As an Expense Tracker (App User), I should see a modern, component-based interface consistent with the "AI-Enabled Finance Tracking" design system so that the app feels coherent and legible. (explicit)
FR-11 — Strictly offline operation. As an Expense Tracker (App User), I should be able to use the app with zero internet permissions, no login, no cloud sync and no Firebase so that nothing about my spending leaves my device. (explicit)
FR-12 — MVVM separation. As an Expense Tracker (App User), I should have the SMS Broadcast Receiver, the Repository and the UI kept in strict MVVM separation so that capture, data and presentation stay reliable and independently correct. (explicit)
FR-13 — UPI field extraction. As an Expense Tracker (App User), I should have amounts, dates and merchant names extracted from standard Indian bank UPI SMS formats — SBI, HDFC and ICICI — so that captured entries are accurate without my intervention. (explicit)
FR-14 — Permission grant and permission-state visibility. As an Expense Tracker (App User), I should be able to grant READ_SMS and RECEIVE_SMS from within the app and see whether they are currently granted, so that I know whether automatic capture is active. (required_inference)
FR-15 — Unparsed-SMS notice and manual fallback. As an Expense Tracker (App User), I should be told when an incoming SMS could not be parsed, so that I can record that transaction manually instead of losing it. (required_inference)
FR-16 — Capture feedback in the ledger. As an Expense Tracker (App User), I should see a newly captured SMS entry arrive in the ledger with a clear source marker, so that I can tell automatic capture apart from what I typed. (required_inference)
Product context. This person pays for everyday things by UPI and receives the bank's debit and credit SMS for each payment. They want a record of that spending without maintaining it by hand, and they have chosen this app specifically because it is offline: no account, no sync, no service holding their transaction history. They are pragmatic and slightly anxious about money — the register must feel like a trustworthy instrument, with honest controls and precise numbers.
Primary goal. An accurate, fully offline transaction history assembled automatically from UPI SMS, correctable by hand where needed, and readable as spending insight — with no login and no cloud sync at any point.
Distinct accepted responsibilities.
READ_SMS and RECEIVE_SMS so automatic capture can run, and understand when it is not running.Relevant inputs and decisions. The amount, date, merchant and category of a transaction; whether a row is a debit (expense) or credit (income); whether a captured entry needs correction; whether an entry should be deleted; which grouping of spending to inspect in the charts.
Interactions with other participants. None inside the product. The persona is the sole human actor; the bank's SMS is an inbound message, not a participant, and no counterparty, administrator or support role exists. The persona's own actions on one destination (capture, correction, deletion) are what the other destinations reflect.
Observable success. The ledger contains every UPI payment the user made, tagged by source, with correct amounts, dates and merchants; manual corrections stick; deleted entries are gone; the charts show the same totals the ledger implies; and the app has never asked for a login or a network connection.
READ_SMS and RECEIVE_SMS appears.The visual direction is Dieter Rams' "Less, but better" — an instrument panel for your money. Nothing decorative that is not also functional; one signal colour that means "money moved"; a strict grid where every amount aligns to a ruled column.
Colour tokens (light mode).
| Role | Hex | Use |
|---|---|---|
| Background | #F2F0EB | Warm off-white ground, ~65% of every screen |
| Surface | #FBFAF7 | Panels, sitting on the ground with a 1px hairline instead of a shadow |
| Hairline | #D8D4CA | 1px panel and row borders |
| Text / Primary | #1C1B18 | All type, rules, the instrument band, primary controls |
| Accent (signal) | #E2571E | Braun-signal orange: primary CTA and the "auto-captured" state dot; at most twice per screen, never a large fill except the single hero ticker strip |
| Muted | #8A867C | Labels, timestamps, secondary metadata |
| Income / credit | #2F6B4F | Deep green, used only for income/credit values so debit and credit are colour-coded like Braun controls |
Typography. Headings and body both use Archivo — a grotesque with a slightly condensed, mechanical cut. Headings at weight 600–700 with tight tracking (−0.02em), flush-left ragged-right, numerals tabular and set large. Every label is 12px small caps with +0.12em letterspacing ("AMOUNT", "MERCHANT", "SOURCE"), so the UI reads like a well-printed instrument panel. Body is 16px with 1.55 line-height.
Type scale (1.25 modular). 12 / 14 / 16 / 20 / 25 / 32 / 40 / 56 / 72. Display headline clamp(40px, 7vw, 96px); section heads 32–40px; amounts 25–56px tabular; labels 12px small caps; body 16px.
Shape language. Modular rounded-rectangle controls with a consistent 10px radius and 1px hairline borders, no drop shadows. Ruled data rows with aligned label/value pairs, chunky 2px dividers separating sections, and circular gauge rings for the insight charts. Hard edges everywhere else — square panels, square status pips, square colour swatches — so the only curves are functional (buttons, gauges).
Layout. A strict 12-column grid with 24px gutters and 64px outer margins on desktop, collapsing to a single column at 375px. The ledger is the spine: a full-width ruled table of transactions with columns for date, merchant, source (SMS/manual), category and amount, each row separated by a hairline and every amount right-aligned into a tabular column. Labels sit above values in small caps, Braun-style. A persistent left rail carries the five destinations as numbered 01–05 items in small caps with a 2px charcoal rule — the rail is the only chrome.
Imagery. Diagrammatic line art only: a thin-stroke schematic of an SMS bubble flowing through a parser into a ledger row, drawn in charcoal on off-white with an orange arrow; pictograms for categories (fuel, groceries, transport, utilities) drawn on a 24px grid with 2px strokes. No photography, no stock people, no illustration for its own sake.
Accessibility. Readable text and controls stay whole at 375px, 768px and 1280px, wrapping or scaling to fit and never covered by another element. Debit and credit are distinguished by colour and by sign/label so the meaning survives without colour perception.
The public entry is not a centred SaaS hero — it is an instrument panel. The first screen is composed in three horizontal registers:
#1C1B18 across the top third carries an oversized flush-left headline in off-white — "YOUR MONEY, ON YOUR DEVICE" — at clamp(40px, 7vw, 96px), spanning nine of twelve columns. Beneath it sits a single line of body copy, and a solid orange rectangular CTA, "Record an expense", is pinned to the baseline. No centred text, no gradient.#E2571E rule runs between band and ledger, reading "SBI · HDFC · ICICI · UPI DEBIT · UPI CREDIT" in 12px small caps — the only large use of the signal colour on the page.The concept is implementable with the accepted content alone: the headline, the CTA into Add Entry, the permission status, and the real ledger excerpt are all existing states and controls, recomposed into an instrument panel. The ledger spine — ruled label/value pairs with amounts right-aligned into a column that lines up across the whole page — is the signature that carries from Landing through Transactions, Entry Details and Insights.
Interaction Model: Animated Motion Tempo: restrained Hero Dimensionality: flat
Landing Hero Motion Brief.
prefers-reduced-motion the ticker becomes a static rule with the same copy set flush-left, the digit-tick resolves instantly to its final value, and all state changes are instantaneous. Every item remains fully readable.READ_SMS and RECEIVE_SMS are the only sensitive permissions required, and they are required solely for SMS-based entry. (explicit) Rationale: capture is the primary trigger; nothing else needs message access.READ_SMS and RECEIVE_SMS permissions. (explicit)READ_SMS and RECEIVE_SMS are required for SMS-based entry; without them, manual entry remains fully available. (explicit)No completed page designs yet.
Completed design pages will appear here when they are ready to preview.
No completed page designs yet.
Completed design pages will appear here when they are ready to preview.
No comments yet. Be the first!