Money Check
Tech stack: Kotlin and Jetpack Compose on Android, SQLite on the phone, an Android accessibility service for the cart reader, and a Python receiver on my Mac that the phone syncs to over Wi-Fi. Built with Claude Code.
I knew I've been ordering a lot, I just didn't know how much. Apps that I tried in the past weren't giving me the accountability and granularity I was looking for to really incite a change. So, Claude and I got to work and whipped up an app that reads my bank SMS and works it out.
It's completely personalized for me. It knows my bank, the four apps I order most from, how much rent I pay and keeps me accountable.
The numbers it exposed have been eye opening to say the least, but it helped me get my act together.
Free guilt trips
Every payment I make comes as an SMS. The app reads it, labels it if it's obvious (Zomato = food delivery), and asks me if it's not.
Immediately when I open a cart on Zomato or Blinkit, before I pay, I get a notification. Cart total, where the week stands against the cap that I've set (8500), and a count of my repeat offenders. "4 soft drinks (21 this week)". 21. I can still proceed with the payment, I just feel bad about it now.
This cart: ₹587 on Blinkit · 8 items
2 energy drinks (17 this week) · 4 soft drinks (21 this week)
The cart reader
The bank SMS only says ₹643 at Zomato. I wanted to know it was butter chicken and garlic naan at 11pm.
Delivery apps don't send receipts, so the app reads the screen instead. No screenshots, no recording. It uses the same access a screen reader for blind users gets, which is the text on the screen, and when a checkout page opens it picks out the items, quantities and prices from that text.
It doesn't care whose app it is. On Zomato, Blinkit, Zepto and Swiggy it kicks in on its own the moment the checkout opens. For everything else there's a Read cart button in the phone's quick settings, the pull-down toolbar next to Wi-Fi and torch. Open any cart, pull down, tap it, and it reads whatever is on screen.
When one of the apps redesigns its checkout I push a fix from my Mac over the next sync.
It's been wrong. It once logged my delivery address as an item, and another time it read the restaurant menu hiding behind Zomato's payment sheet as my order. Every one of those screens is now a test, 14 real ones from my phone.
Read 2 items · ₹643.68 on Zomato
Butter Chicken (boneless), Garlic Naan
This week
What you bought
Every item the cart reader picks up gets sorted into a group: energy drinks, soft drinks, protein bars, sweets, cat stuff, household and so on. The panel adds up each group for the current payday period, both how much I spent and how many I bought. That's what turns a pile of orders into habits I can actually see.
Tapping a group shows every purchase behind it, with the app and the date. That's how I found out that my soft drink spend is basically all Diet Coke from Zepto, bought every couple of days in packs of two or four, ₹2,694 since payday.
Insights
What you bought
Soft drinks
How Claude fixes it
The app syncs to my Mac over Wi-Fi every 30 min and after every payment, so Claude sees what I see. I complain in chat ("latest order still has wrong quantities and my address"), it reads the saved screen, fixes the parser, adds a test, builds, publishes. The phone installs it on the next sync. Data fixes show up in the app under "Changed by Claude" with an Undo button.
That's 25 versions in two days. The last few were because scrolling felt laggy. A CPU profile showed the app rebuilding its accessibility tree every frame because of its own cart reader, which 0.24 fixed.
Labels
Teaching it how I live
I gave it a dataset worth three months of statements and a few hundred SMS'es. Rent shows up as a ₹42,000 transfer with no name on it. Card bills get paid from savings on salary day, and it matches both sides so that doesn't count as spending. Salary is income, work pays for the AI subscriptions so those are excluded, and one-off stuff stays out of the averages.
Every time I answer a question, that answer shows up first next time. "Always label this like that" turns it into a rule, which I can see and delete under Labels.