LiteRT-LM

Activity, over time

Daily observations from the public event archive.

Attention

Daily stars & forks

Loading attention chart

Star accumulation

Cumulative stars

Loading star accumulation chart

Pull requests

Daily opens & merges

Loading pull requests chart

Conversations

Daily issues & comments

Loading conversations chart

Community

Daily accounts & reviews

Loading community chart

Releases

Daily releases

Loading releases chart

Mentions

6posts · 30d
Nov 30 posts
  1. Booji: the ghost who reads my bank SMS, buries my zombie subscriptions, and tells me what next month will cost This is a submission for the Hacktoberfest Open-Source AI Challenge: Week 4, theme: Ghost in the Machine. What I Built The ghost of a chartered accountant, your bank SMS, and nothing leaving the phone. I've always believed that if you believe you have enough money, the universe will find a way to give you that money. Delusional, right? Well, the ghost living in my house thinks so too. He keeps watching me go out every weekend, browse and order things online, and on the rare days when I'm actually home? Order in. I think this has given him anxiety. And he's doing something to me. :/ I've started noticing that all my transactions have been categorised for me. The subscriptions I'd forgotten I was paying for have been highlighted. And now there are notes on my phone telling me how to plan for next month?! That's it. I'm trapping him on my phone. He wants to make a ledger so badly? He's going to have to do it for the rest of his afterlife. Meet Booji. Booji is a ghost accountant who lives on your Android phone. He reads the SMS messages your bank already sends after every debit and credit, and takes you on three rather uncomfortable visits: - The Ghost of Spending Past: Booji digs through your transactions and categorises them into jars, one for each kind of spending. Each jar fills up according to its share of your expenses. Tap one to see exactly what went into it, which merchants kept coming back, and where your money went. Every transaction gets a category that you can correct with a tap. - The Graveyard — The Ghost of Spending Present: Not every haunting belongs to the past. Some charges are very much alive, returning month after month whether you remember them or not. The Graveyard is where Booji lays them to rest. Every recurring charge gets a headstone bearing its name and monthly cost. You decide whether it is alive (something you still use) or a zombie (a subscription you've long forgotten but are still paying for). Booji keeps a running tally of what these financial zombies are costing you. - The Future — The Ghost of Spending Yet to Come: Finally, Booji peers into a crystal ball. It looks at your spending patterns, upcoming recurring charges, income and even the Indian festival calendar to estimate what the rest of your month might look like. It shows you what the month is likely to cost, the range of possible outcomes, and where your balance might land. If a festival is approaching, it sets aside an allowance rather than pretending the next few weeks will look like an ordinary month. It also shows you what's eating into your money and what you could save by making a few changes. The idea isn't to frighten you into spending less. It's to give you enough clarity to spend without constantly worrying about what your bank balance will look like tomorrow. Booji wants you to have money left over, and the freedom to say yes to the occasional nice thing without having to check your balance first. After all, even a ghost needs a purpose. Booji just happens to have found one in your finances. Why A Christmas Carol, beyond the costumes? Because Dickens wrote a ghost story about money, and I seem to have found my own version of it in the average Indian bank statement. There are the ghosts of purchases past, the subscriptions that refuse to die, and the rather uncomfortable possibility of what happens if we keep going the way we are. Booji simply makes those ghosts visible. And, much like Scrooge, you get a chance to do something about them. Demo !Booji demo: the Past tab, two zombies buried in the Graveyard, and Booji writing its note on the Future tab | Past | Graveyard | Future | |---|---|---| | !Past tab: the month's total by lantern light and the honey jars | !Graveyard: a moonlit sky over headstones, two marked zombie | !Future: Booji looking into the crystal ball at what the month will need, with Dussehra and Diwali ahead | | The festival allowance | What is eating the month | |---|---| | !Dussehra tomorrow and Diwali in twenty days, with the allowance set aside for each | !The list of what is eating the month | | SMS the rules could not read | One of them, read by Booji | Booji reviews the month | |---|---|---| | !Five bank SMS the regex rejected, waiting for Booji | !A Kotak card spend Gemma read out of an unparseable SMS, with the limit left and a button to drop it if misread | !The note Gemma 4 wrote on the phone about October | | Booji reads the headstones | Booji looks ahead | Inside a jar | |---|---|---| | !Booji naming the zombies and asking about the undecided | !Booji on what the month will need, the festival allowance and what is left over | !Tap a jar: who kept coming back and every transaction in it | Judges: the APK is available on the releases page. Switch on Demo inbox in Settings and Booji will populate itself with three months of synthetic HDFC-format bank SMS, processed through exactly the same parser used for real messages. No Indian bank account required. With real SMS, Booji tracks every account that sends a balance notification and combines them. I currently have three accounts in mine. Code ShalinaBhargava/booji MIT licensed. Kotlin, Jetpack Compose, no backend, no accounts. How I Built It A ghost accountant sounds like a fairly specialised job. Fortunately, I had Gemma. Giving the ghost a brain: the open-weight model. Booji runs Gemma 4 E2B directly on the phone through LiteRT-LM, Google's on-device inference runtime. The weights are the Apache 2.0 release on Hugging Face, weighing in at 2.6 GB. They're downloaded once from inside the app over Wi-Fi (or can be pushed using adb). Inference runs on the GPU first, with the CPU as a fallback. Gemma has three responsibilities. Two of them are particularly important because they are things a regular expression simply cannot do reliably. 1. Reading the messages the rules cannot. Three banks and a card provider each have their own peculiar way of describing a transaction. The regex parser handles the formats it knows, but anything it doesn't recognise used to be discarded. Now, if a bank-looking SMS contains a rupee figure but cannot be parsed, Booji sets it aside for review. One tap sends up to six messages to Gemma, which extracts the amount, direction, merchant, account, balance and card limit as JSON. Each message is read only once. The result is saved as a transaction marked read by Booji, with an option to discard it if the model gets something wrong. On my own inbox, this made a considerable difference. Instead of being limited to the four bank formats my parser understands, Booji can now make sense of the messages that fall outside those rules. 2. Making sense of merchants. Some merchants are easy to identify. Swiggy is food, Netflix is a subscription, and anything containing the word "rent" is probably rent. Keyword rules handle these obvious cases. The rest go to Gemma in batches. It receives a list of merchant names and returns their categories along with cleaned-up names. So "Blinkit Commerc" and "Blinkit" become the same merchant across the jars, the graveyard and the recurring transaction history. There is an additional safeguard: a cleaned-up name is only accepted if it visibly shares its beginning with the original. This prevents something like "Mom" from accidentally being merged into "Transfer". And, importantly, your corrections always take precedence. Once you correct a merchant, Booji remembers that preference for future transactions. 3. Giving the ghosts a voice. Each tab has a Summon button. Press it, and Gemma receives a small JSON containing the numbers Booji has already calculated: monthly spending, top categories, recurring charges, zombie costs, forecast ranges, remaining money against the monthly goal, and the biggest drains on the budget. It then turns those facts into three short paragraphs in character. The model isn't asked to analyse your finances from scratch. It is asked to interpret the numbers and explain what they mean, in the voice of a rather opinionated dead accountant. The architecture: a haunted house with no landlord. Booji is a single Android application with no backend. Everything that happens before the model is deterministic Kotlin. The model only sees finished facts, and only when you explicitly ask it to do something. flowchart TB SMS[(Bank SMS inbox)] --> Reader[SmsReader<br/>keeps bank-looking senders] Reader --> Parser[BankParser<br/>regex: amount, direction, merchant,<br/>account, bank, balance, card limit] Parser -->|parsed| Mark Parser -->|unreadable, has a rupee figure| Unread[Set aside] Unread -->|one tap, six at a time| Gemma Gemma -->|JSON facts, cached per SMS| Mark Mark[markCards → markCardPayments →<br/>markOwnTransfers → your overrides] Store[(booji.json<br/>your rules, overrides, notes,<br/>fates, aliases, balances, goal)] <--> Mark Store <--> Ledger Mark --> Ledger[Ledger<br/>summary · balance with roll-forward ·<br/>recurring · forecast · festivals · drains] Ledger --> UI[Past · Graveyard · Future · Settings<br/>Jetpack Compose] Ledger -->|finished numbers as text| Gemma UI -->|Summon / sort / read| Gemma Gemma[Gemma 4 E2B on the phone<br/>via LiteRT-LM, GPU then CPU<br/>reads · sorts and tidies names · writes] Gemma -->|the ghost's words| UI Gemma -->|categories + clean names| Store There are three architectural decisions worth calling out. First, every input to Gemma is text, never a calculation it needs to perform. It receives an unreadable SMS, a list of merchant names, or a JSON containing numbers Kotlin has already worked out. Second, everything Gemma returns is cached. A message is read once, a merchant is categorised once, and the user's own corrections take precedence over the model's suggestions. Finally, there is exactly one network connection in the entire application: the initial model download from Hugging Face. After that, everything happens on the device. The accountant does the accounting. Every number in Booji is calculated in Kotlin from the SMS data. Gemma is deliberately kept away from the actual arithmetic. Here's how the ledger works: - Parsing: Regular expressions extract the amount, transaction direction, counterparty, account tail and quoted available balance. OTPs, future debit reminders, card bill due notices, offers and payment requests are rejected. If both a UPI app and the bank send an SMS about the same payment, the message containing the bank balance takes precedence. - Balance: Each account starts with the latest balance reported by the bank. Every subsequent debit is subtracted and every credit added. This is necessary because banks often send transaction notifications without including the updated balance. Bank account balances are combined, while credit cards are tracked separately using their available limits. They are never added to the bank balance. Card bill payments and transfers between your own accounts are matched and excluded from spending to avoid double-counting. No bank API, no Account Aggregator, no credentials. The bank has already sent the information in a text message. - Corrections: Tap any transaction to see its original SMS and the reason it received its category. You can change the category for that transaction or for every transaction from that merchant, mark it as a transfer between your own accounts, ignore it, correct its direction, or add a note. Categories can also be renamed, and new ones can be created. - Recurring charges: Booji looks for transactions from the same merchant, with amounts within 15% of the median, and gaps clustering around a week, a month or a year. A pattern must appear at least twice before it is considered recurring. - Forecast: This is where the crystal ball gets its numbers. Booji looks at discretionary spending over the previous eight weeks and groups it into seven-day windows. The median window determines the expected daily spending pace, while the 25th and 75th percentiles establish the forecast range. This prevents one particularly expensive weekend from distorting the entire month's projection. To that, Booji adds recurring charges still due before month-end and the expected salary if it hasn't arrived yet. The projected amount needed for the month is the spending already recorded plus these remaining expenses. The expected leftover is then calculated from income minus that amount. - Festivals: A normal week and the week before Diwali are rarely comparable. A forecast that ignores the festival calendar is going to be least accurate precisely when you need it most. Booji includes a small calendar of Indian festivals, covering national festivals and the region selected in Settings. When a festival or its run-up falls within the remaining month, the forecast adds a named allowance. If your SMS history contains the same or a comparable festival, Booji estimates the additional spending using your own previous spending patterns. Otherwise, it uses a stated typical multiplier and makes that distinction visible in the allowance card. There are two things it deliberately refuses to do: guess what people in your location might spend (which would require external data), or make assumptions based on your gender. Neither belongs in a personal financial forecast. There are unit tests covering the parser (HDFC, SBI, ICICI, Axis and PhonePe formats, along with rejected messages), ledger calculations against the demo inbox, and festival allowances. Building the haunted house. I wanted the interface to feel like something out of a Christmas ghost story rather than another spreadsheet pretending to be an app. The headings use Henny Penny and the body text uses Mystery Quest, both Google Fonts released under the SIL Open Font License, so they're safe to ship with the project. Booji itself is a little witch. The accountant apparently took up hats in the afterlife. It floats alongside the ledger in the Past tab, appears under a full moon in the Graveyard, and stands with its back to you at the foot of the crystal ball, peering into the future. There's also a spider that lowers itself on its thread when you start scrolling. The tombstones are real stone, and the jars are filled with honey. On my own phone, the first run parsed 530 bank SMS messages across four months and reconstructed the account balance from the latest available notification. The screenshots above use the demo inbox, though. My actual bank statements are nobody's business, which is rather the point. Teaching a 2B model to copy, not count. One of the more interesting challenges was getting a relatively small model to behave reliably when writing financial summaries. The first versions of Booji's notes were almost right. Almost was the problem. A month-over-month difference would be calculated incorrectly by a few thousand rupees. An undecided recurring charge would be confidently labelled a zombie. A negative number would be read aloud as if it were good news. The solution was surprisingly consistent: stop asking the model to calculate anything. Every time Gemma made a numerical mistake, I moved that calculation into Kotlin and gave it the finished result instead. Amounts arrive pre-formatted as ₹40,296. Differences arrive as ₹7,213 less than September. The Graveyard data arrives as three separate lists, and the leftover amount is already calculated and formatted. After that, the ghosts stopped getting their sums wrong. Mostly because they stopped doing sums altogether. This is the same principle I used last week for Slothie, and it's one I would apply to any assistant working with personal data: The model is a reader, a sorter and a writer. Never the accountant. If a figure isn't in the JSON, it doesn't appear in the note. The system prompt explicitly enforces this, and because the weights, prompts and code are open source, anyone can inspect that decision rather than simply taking my word for it. Why Does Open Innovation Matter? A bank statement is probably one of the most personal documents on your phone. It tells a story about where you live, who you pay, what you eat, what you spend your weekends doing, and sometimes how much money you have left. I wasn't comfortable uploading that information to a third-party service, including a model provider. I also didn't particularly want a budgeting application charging me a subscription to point out all the subscriptions I was already paying for. Open weights made it possible to solve both problems. Gemma runs directly on the device, which means Booji can work in airplane mode. The INTERNET permission exists for exactly one purpose: downloading the model weights. There's no API key to leak, no per-token bill, no rate limit and no dependency on a third-party service staying available. The Apache 2.0 weights, open-source runtime and prompts in the repository also mean that the model's responsibilities are inspectable. The promise that Gemma doesn't invent financial figures isn't just a marketing claim. You can look at the code and see how the information is prepared before it reaches the model. A closed API might have written a wittier ghost. But it would have required my bank messages to leave the phone, and that was a trade-off I wasn't willing to make. There's also a practical advantage to running an open-weight model locally. Reading a hundred unparseable bank messages and sorting a hundred merchants means a handful of inference calls, cached per message and per merchant. They cost nothing to run again when I correct something. With a paid API, that would mean additional costs, rate limits and sending the most sensitive data in the application to an external service. For Booji, open innovation wasn't simply a way to use an open-source model in a hackathon. It was what made the application possible in the first place. My Agent Session Booji was built in one night using Claude Code. I started with the SMS parser and its tests, moved on to the ledger calculations, then built the three screens. The final stretch was spent teaching a 2B model to copy numbers instead of trying to calculate them. It was a fairly concentrated session of building, testing, finding things that didn't work, and making the ghost a little more useful. The Spirit of It There's something rather Dickensian about a chartered accountant who dies with perfectly balanced books and ends up haunting someone whose books are anything but. Booji gets three visits: the spending that has already happened, the charges that keep returning in the present, and the future that is slowly taking shape through our everyday decisions. The first time I opened the Graveyard with my demo inbox, I found two tombstones I'd planted deliberately: an abandoned gym membership and a design suite subscription. Together, they were costing more per month than my groceries. That was the moment the idea clicked for me. The Ghost of Christmas Past in Dickens's story forces Scrooge to confront the choices that brought him here. The Ghost of Christmas Present shows him the world continuing around him, including the consequences of his indifference. And the Ghost of Christmas Yet to Come shows him a future that is rather less appealing than he imagined. Booji follows the same progression, only with bank SMS instead of memories, subscriptions instead of gravestones, and a crystal ball powered by Kotlin. But there's one important thing about the future in A Christmas Carol: it isn't inevitable. Scrooge gets to change it. So do we. Bury the zombies, understand where the money is going, and make a few different choices. The future moves. And Booji, for all its afterlife accounting experience, is quite happy to help. Prize Categories - Best Use of Gemma

  2. Slothie: an on-device Gemma 4 sloth that learns your risky phone hours This is a submission for the Hacktoberfest Open-Source AI Challenge: Week 3, theme: Slow and Steady. What I Built Slothie is a sloth that lives in your Android phone. It works out the hours of the day when you are most likely to reach for the phone, tells you which apps are behind each one, and a few minutes before those hours it sends a nudge with a one-tap nap: Do Not Disturb, held for 30 or 60 minutes, released by itself. The whole thing is built around one lazy idea: the phone already knows. Android keeps a log of which app was in front and when. Slothie reads that log on the device, slices it into a day-by-hour grid, and asks a simple question of each hour: on what share of days did you spend five or more minutes here? Rank by that chance times the usual minutes, skip the neighbours of a stronger hour, and you get your three risk hours. Mine, the morning I took the screenshots: 2 AM (85% of days, YouTube, Chrome and Netflix), 6 PM (83%, WhatsApp, Chrome and YouTube) and midnight (85%, Chrome, YouTube and WhatsApp). Yesterday these times looked like 3 AM, 7 PM and 10 PM. To get the mood of the hour, slothie shows 4 moods: asleep when you are barely on the phone, chill at your normal pace, one eye open when a risky hour is close, wide awake with a sweat drop when you are well past normal. Why not digital wellbeing: The digital wellbeing app gives statistics that might sound scary but also unattainable and actionable, the gentle nudge with slothie actually finds the pattern and gives you a decision. Demo !Slothie demo: the Today tab, the sloth waking up, a risk-hour nudge and a one-tap nap | Today | Hours | Apps | |---|---|---| | !Today tab: the sloth, pace against your usual, and the note Gemma wrote | !Hours tab: the three risk hours with their odds and the apps behind them | !Apps tab: Gemma sorted the apps into categories, with manual overrides | !A word from Slothie: the note Gemma 4 wrote on the phone, from the numbers above it Code {% embed ShalinaBhargava/slothie %} MIT licensed. Kotlin, Jetpack Compose, no backend, no accounts. How I Built It The open-weight part. Slothie runs Gemma 4 E2B on the phone through LiteRT-LM, Google's on-device runtime. The weights are the Apache 2.0 release on Hugging Face, 2.6 GB, downloaded once from inside the app over Wi-Fi (or pushed with adb). The GPU backend is tried first and CPU is the fallback. On a Pixel 9 the first load takes a few seconds and the note streams in at a comfortable reading pace. Gemma does two jobs: 1. Writes the sloth's note. It receives a small JSON of integers and app names (today's minutes, usual minutes by this time of day, pickups, longest break, the three risk hours with their odds and apps) and is asked for three short paragraphs: the riskiest hour, one lazy thing to do about it tonight, and one honest line about today. 2. Sorts your apps. It gets the list of app labels and package names and returns a JSON array of categories: Feed, Video, Game, Chat, Tool, Work, Health. Your own choices always override it. The deterministic part. Every number is computed in Kotlin from UsageStatsManager.queryEvents. One app is in front at a time, so a resume closes whatever was open; screen-off and lock close everything; blips under three seconds are dropped. Sessions are sliced at hour boundaries into a minutes[day][hour] matrix. Hours that have not happened yet today are not counted as observed, so the morning never drags down the evening. There are unit tests for all of it. The split is deliberate and it is the same rule I would apply to any assistant that reads personal data: the model is a writer and a sorter, never an accountant. If a figure is not in the JSON it is not in the note. The system prompt says so, and because the weights and the prompt are both open you can check. Nudges and naps. Inexact AlarmManager windows, twelve minutes before each risk hour, re-armed daily and after reboot. The nap action flips NotificationManager.setInterruptionFilter to priority-only, schedules its own end, and posts an ongoing notification so you can end it early. No accessibility service, no overlay, no app blocking: a sloth does not fight you, it just makes the quiet option the easy one. Why Does Open Innovation Matter? A phone usage log is a diary. It says when you sleep, what you doom scroll, when you are at work and when you are not. I did not want to upload that to anyone, including a model provider, and I did not want to pay per token for a sloth to tell me to go to bed. Open weights make both problems go away. The model is on my phone, so the app works in airplane mode and the INTERNET permission exists for exactly one request: downloading the weights. There is no key to leak, no bill, no rate limit, and no terms-of-service change that can hid it behind a paywall. And because the weights are Apache 2.0, the prompt is in the repo, and the runtime is open source, the promise "the model never invents a number" is something you can read rather than something you have to trust. A closed API could have written a nicer paragraph. It could not have done it on a phone with the radio off. The Lazy Payoff Slothie measures its own payoff with the same log it reads. The "Longest break" tile on the Today tab is the longest stretch since your first pickup without the phone; the risk-hour odds move as the days roll. The honest number from day one on my phone: 7 h 49 m by mid-afternoon against a usual 3 h 50 m, which is why the sloth was wide awake and asked to be put down. Effort saved: the three risk hours and the apps behind them took Android's own log plus about forty lines of Kotlin to find. I had never noticed the 3 AM one. Prize Categories - Best Use of Gemma

  3. Creative Detour: I Made My Phone Help Me Live More, Scroll Less (On-Device AI) This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass What I Built Creative Detour is an Android app that waits for the right moment in your day, then nudges you to go move a little. And the nudge is written by Gemma 4(E2B variant that is designed for mobile like devices), running entirely on your phone. Quick confession first. My original idea was way more evil 😈: an app that warns you about screen time and if you ignore it, escalates. A friendly warning > facts and restrictions > literal emotional blackmail. I wanted things that new android versions actually restricts and its not easy to plan their workarounds. Also, play store hates that even more. So I dropped that idea. I also toyed with a bird-call identifier for trails, but that only helps people who already go hiking or somewhere outside regularly(based on my understanding). And if you're on a trail, you're probably not doom-scrolling anyway. So I flipped the question: nobody has time to "use" a wellness app. What if the app just noticed the gaps in your day and used those? <h3>A quick look at Creative Detour</h3> <table> <tr> <td width="33%"> <img src="dev-to-uploads.s3.us-east-2.amazonaws.com" alt="Creative Detour home screen" width="100%"> </td> <td width="33%"> <img src="dev-to-uploads.s3.us-east-2.amazonaws.com" alt="Creative Detour activity nudge" width="100%"> </td> <td width="33%"> <img src="dev-to-uploads.s3.us-east-2.amazonaws.com" alt="Creative Detour app interface" width="100%"> </td> </tr> </table> Here's what Creative Detour actually does: - 👀 Watches your day, on-device. Step counter, screen-on time (only screen on/off, never which apps), your next calendar event(if they exists) and the time of day. - 🤔 Decides if now is a good moment. It nudges when you've been sitting (under 400 steps an hour) or glued to your screen (30+ minutes in the last hour). It stays quiet when you're already moving, when a meeting starts in under 10 minutes, when you're away from your phone, or at night. (It tries to pick time in between your activities if you have events set in your calender. It will not suddenly start notifying while you are in a scheduled meeting) - 🎲 Picks an activity. Not just "go for a walk". There are 9 types: a walk outside, a mindful minute outdoors, a stretch break, a mini workout, a stair climb, one song of dancing, an active chore, a walk-and-talk call, a mobility reset. - ✍️ Gemma writes the nudge. It gets the moment, the activity and your last few nudges (plus what you did with each), so it doesn't repeat itself. - 🔔 You answer with one tap. Let's go or Not now, then a one-tap reason: Busy, Tired, Bad weather, Already moved. - 🧠 It learns. "Tired" means gentler suggestions and 3 hours of space. "Bad weather" means indoor ideas only. Say "Busy" twice at the same hour and it stops nudging you at that hour. Activities you actually do come up more often. - 🤫 It explains itself. The home screen and the ongoing notification always say why it's quiet ("You said 'Tired'. Giving you some space") and when it'll check again. The moment it clicked for me: I deliberately packed my calendar with fake events to test it. The nudges landed exactly in the free gaps between them, never right before an event. I've been using it since. Demo 📦 Grab the APK: Creative Detour v1.0.0 on GitHub Releases Install it, walk through setup (every permission is explained before Android asks) and optionally download the AI model in the app (2.6 GB, Wi-Fi only). Without the model it still works, with hand-written nudges. Code {% github KrAG2000/creative-detour %} Kotlin, Jetpack Compose, Room, MIT licensed. How I Built It The stack - Kotlin + Jetpack Compose, native Android. Nearly everything here is a native API (sensors, usage stats, foreground services, notification actions), so React Native would just have been a bridge to fight. - Gemma 4 E2B (gemma-4-E2B-it.litertlm, Apache 2.0) from the litert-community repo on Hugging Face - LiteRT-LM, Google's on-device LLM runtime, through its Kotlin API - Room for the nudge history, DownloadManager for the model Fun fact: I didn't know Kotlin when I started. It felt like TypeScript wearing a Java jacket. Coroutines clicked once I mapped them to Node: the Android main thread is basically the event loop and Dispatchers.IO is roughly libuv's thread pool. Except Kotlin has real threads, which is both amazing and terrifying. The engine decides when, the app decides what, Gemma decides how to say it My first version let the model do everything. The result? Three nudges in a row: > "Time to stretch your legs for a quick, refreshing walk…" > "Time for a quick walk outside to get some fresh air…" > "Time to gently stretch your legs outside for a few minutes…" Same nudge, three outfits. 😅 Small models fall into grooves and my prompt literally said "get the user to move, ideally outside", which a 2B model reads as WALK. So I split the job: 1. NudgeEngine (pure Kotlin, unit tested) decides whether to nudge and it always returns a reason: sealed interface Decision { data object Nudge : Decision data class Quiet(val reason: String, val retryAt: Long? = null) : Decision } 2. ActivityPicker picks what: hard filters from your recent answers (tired → gentle only, bad weather → indoor only, busy → 3 minutes max, never a repeat of the last two types), then a weighted random pick that learns: // "Busy" or "bad weather" isn't the activity's fault; only count real disinterest. val outdoorBoost = if (activity.outdoor) 1.5 else 1.0 // it's a touch-grass app return outdoorBoost * (1.0 + accepted) / (1.0 + 0.5 * declined) 3. Gemma just writes it up. The prompt gets the facts, the chosen activity with example ideas and the last 4 nudges with what I did ("they did it", "declined (tired)", "ignored it"). Plus one rule I'm weirdly proud of: Never start with "Time to". Nudges went from "walk walk walk" to "Put on one song and dance like nobody's watching". Way better. The adaptive scheduler The background service doesn't just tick every 10 minutes. After each check it plans the next one: 45 minutes after a nudge, at the end of a cooldown (3 hours after "Tired"), otherwise ~10 minutes. Unlocking your phone triggers a check after 2 minutes of use, because "back on the phone" is exactly when a nudge matters. The ongoing notification always shows "Next check around 4:20 PM" and the current reason. Running a 2.6 GB model on a phone without things exploding This was the most fun (read: painful) part. Load → generate → unload, every time. Keeping ~2 GB resident in a background service would get the app killed by Android and nudges are 45+ minutes apart anyway. GPU or CPU? Don't guess, measure. My Pixel 9's GPU happily loaded the model, then died on the first real token: LiteRtLmJniException: Failed to call nativeSendMessage: INTERNAL: └ Failed to invoke the compiled model GPU support for this model build just isn't there yet on that chip. Specs can't tell you that, so the app now runs a one-time capability test: it writes a real sample nudge on GPU, then on CPU, times both and keeps the fastest one that actually works. Too slow (over 45 s)? Templates instead. A 32-bit phone or under ~4 GB RAM? It doesn't even offer the 2.6 GB download. Free memory gets checked before every nudge too. The result is saved per model + OS build, so a system update (new GPU drivers) triggers a re-test. On my Pixel 9, CPU wins: about 11–18 seconds per nudge. That sounds slow until you remember it happens in the background. Nobody's watching a spinner. And if anything fails, you still get a nudge. A FallbackNudgeWriter drops to hand-written templates on any error. Two tricks I didn't know existed - Download verification for free. The model downloads straight from Hugging Face (Apache 2.0, public, resumable), Wi-Fi only. Hugging Face's x-linked-etag response header is the file's SHA-256, so the app verifies all 2.6 GB before ever loading it. - android:manageSpaceActivity. I clear app storage a lot and every time it nuked the model. With this one manifest attribute Android's "Clear storage" button becomes "Manage space" and opens my screen instead: clear history but keep the model, delete the model, or delete everything. "AI-assisted", not automated (I feel it is more on "Fully autonomous" side) I built this with Claude Code as a pair programmer and Kotlin teacher. It wrote big chunks of code, but I tested every build on my phone, ran every adb command myself, pushed back on plenty of suggestions and made the calls on what the app should be. The full session is below if you want to see the messy middle. Why Does Open Innovation Matter? Look at what this app reads: your calendar, your screen habits, your step count and your own explanations of why you didn't want to move. That's some of the most personal data on a phone. With a closed API, every nudge would mean shipping that bundle to someone else's server. Every 45 minutes, all day, forever. With Gemma running locally: - 🔒 Nothing leaves the phone. No account, no analytics, no server. The only network request is the one-time model download. - ✈️ It works offline. Airplane mode, a trail with no signal, it doesn't care. - 💸 It costs nothing per nudge. No API bill for a nudge every 45 minutes per user. - 🔧 I control the runtime. The GPU/CPU capability test, load-and-unload memory handling and the fallback chain only exist because the model runs on my terms. A model catalog makes swapping models a one-entry change. - 📜 The license made distribution easy. Gemma 4 being Apache 2.0 meant the app could download it directly from a public URL, no login, no gating. Being honest about where open was harder: a 2.6 GB download is a real ask, GPU support isn't universal yet and 11–18 seconds would be painful for a chatbot. For background nudges, though, every one of those trade-offs is fine. And keeping my calendar to myself is worth way more than a faster nudge. My Agent Session The whole build, from "understand the quest" to the license file: brainstorming, Kotlin lessons, every bug and the GPU saga. {% agent_session building-creative-detour-an-on-device-gemma-4-android-app-that-nudges-you-off-your-screen-asx70m %} Prize Categories - Best Use of Gemma: Gemma 4 E2B runs fully on-device via LiteRT-LM and writes every AI nudge, with automatic GPU/CPU selection and a template fallback. --- Now close this tab and go stand outside for a minute. Your phone will wait. 🌿

  4. DEV · · @nezhar

    Lookabout 🌿 photo walk that Gemma checks on your phone, offline This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass What I Built Lookabout is an Android app that gives you a reason to go for a walk and look around properly. A little walk. A fresh look. Before you leave, you pick how many pictures you want to find (3 to 6), a topic, and what the sky is doing. Gemma 4, running entirely on your phone, writes a short list of pictures to look for, fitted to the topic, the sky, the time of day and the month. Then you put the phone in your pocket and go. !Start Screen of the App When you spot one, the phone's own camera app takes the photo, and Gemma checks it on the spot. Each picture comes with four yes/no questions. Two or more YES answers and the photo is Accepted. Fewer, and the app says Not yet, and you can retake it or skip it. The percentage is simply the share of YES answers, not the model's confidence. The theme asks for the screen to be the shortest part of the experience, so most of the work went into the moments when you are not looking at it: - The verdict is a buzz. One long pulse means Accepted, two short ones mean Not yet. You know the result without reading anything. - The current picture sits on the lock screen as a silent notification, such as "2 of 5". One tap opens the camera. - "Check photos at the end" lets you skip the waiting entirely: shoot, walk on, and let the app check the photos when you finish. - Gemma can be wrong, and you can say so. Tap a question to flip its answer and the verdict follows. The summary then tells you how often you and Gemma agreed. Everything runs on the phone. After the one-time model download, Lookabout needs no internet connection: the lists, the photos and the checks stay on the device. Taking it outside I took it out on a cloudy afternoon, and the video below is that walk. I chose three pictures and the topic Autumn colour, told the app there was some cloud, and let it use my rough location for the light. Then I switched on airplane mode and asked for the list: 1. Orange leaves against a cloudy sky. 2. Brown and red foliage under diffused light. 3. Autumn colours illuminated by afternoon light. I hadn't written any rules about how cloudy weather should shape a photo challenge. Gemma was given "some cloud" and the time of day, and came back asking for a cloudy sky behind the leaves and diffused light on them. The first picture was easy. Orange leaves with the grey sky behind them: three of the four questions came back YES, so 75% and Accepted, about four seconds after I took the photo. The second one made me work. My first tries at brown and red foliage came back as Not yet, so I kept walking and kept looking. What got through in the end was a line of curled brown leaves along a path. Gemma had answered NO to "Is the light diffused by clouds?". I was standing under those clouds, so I tapped the question to correct it, and that took the photo to two of four: Accepted. The third went through first time: an open field under the afternoon sky. The Summary showed three of three accepted and about four seconds per check on my Google Pixel 7a. Gemma and I agreed on 11 of the 12 answers for the final photos. !Final screen with photos Demo {% embed youtube.com %} The only cut is at the start, where I shortened the one-time model download (2.6 GB). At 0:25 I switch on airplane mode. From there the phone is offline and everything is shown at real speed, including each photo check, which takes about four seconds. Want to try it? Download Lookabout 0.1.0. It needs a real phone running Android 12 or later, and on first start it offers to download the model (2.6 GB, Wi-Fi only). Code {% embed nezhar/lookabout %} Lookabout is open source under Apache 2.0 and uses the openly available Gemma model and LiteRT-LM runtime. How I Built It The stack. Kotlin and Jetpack Compose, with Gemma 4 E2B running through LiteRT-LM, Google's open-source runtime for on-device models. There is no inference server and no API key anywhere in the project. Gemma does two jobs. 1. It writes the walk. The prompt carries the topic, the sky, the time of day and the month. If you allow it, the app also works out on the phone where the sun is and adds a line such as "low in the south-west", so the list can ask for long shadows when there are any. Gemma returns each picture together with its four yes/no questions. If an answer comes back incomplete, the gap is filled from a built-in list, which also covers the case where the model is not loaded yet. 2. It checks the photos. Each photo goes to Gemma with its four questions, and the app counts the YES answers. I chose yes/no questions over a free-form "does this photo fit?" on purpose: a small model answers narrow questions more reliably, the result is easy to parse, and you can see exactly which answer you disagree with. Making it hold up on a real phone. This is where most of the time went. - The model loads on the GPU with speculative decoding when the phone can do it, falls back to the GPU without it, and then to the CPU. On phones with 12 GB of memory or more, a debug screen lets you switch to the larger Gemma 4 E4B. - Lookabout has no camera screen. It hands over to the phone's camera app, which means no camera permission, and also means Android is free to close Lookabout while a 2.6 GB model sits in memory. So the walk is saved after every change, and the app reopens where you left off and checks the photo you took in the meantime. - The model download runs through Android's download manager, on Wi-Fi only, and survives the app being closed. - If a check fails, the photo is marked "Not checked" and can be checked again from the summary, so a bad moment never costs you a picture. Why Does Open Innovation Matter? In January, I wrote Beyond Vendor Lock-In: A Framework for LLM Sovereignty, looking at how different ways of running language models affect data control, costs and vendor dependency. The framework goes from closed browser applications to fully self-hosted infrastructure. Lookabout gave me a chance to put those ideas into practice, on a much smaller scale. The framework's highest level of control is self-hosting: running the model yourself instead of depending on someone else's API. With Lookabout, the host is the phone in your pocket. That changes a few things. - Your photos stay yours. A photo walk can capture your street, your neighbours and your kids. Gemma processes the pictures locally. They don't need to leave the phone for AI processing. - No cost per photo. There's no API key, account or inference bill. Retaking a picture five times doesn't cost five times as much. - No inference service to depend on. Once the model is downloaded, generating challenges and checking photos don't require a network connection. No server outage or API rate limit can interrupt the walk. - The implementation is open. Anyone can inspect the code, fork the app or experiment with a different model. The larger Gemma 4 E4B option is already one example of that flexibility. Of course, local inference comes with its own costs. The model is a 2.6 GB download, needs a capable Android phone and takes about four seconds to check a photo on my Pixel 7a. Getting the GPU fallback right and keeping the walk alive when Android closes the app took real work. That's also something I argued in the framework: more control doesn't mean less complexity. It means choosing which complexity you're willing to own. For Lookabout, that trade-off makes sense. The places worth walking to are often the places with no signal. A cloud API would have made the app dependent on connectivity at exactly the moment I wanted people to forget about their phones. The goal was never to build the most powerful image recognition system. It was to build a small, useful experience that works wherever you take it. And on that cloudy afternoon, in airplane mode, it did. Prize Categories - Best Use of Gemma: Gemma 4 E2B runs locally on the phone and does both AI jobs in the app: writing photo challenges based on the conditions outside and checking each photo against four specific questions. The complete experience works offline, with no inference server or API key. Lookabout started as an experiment in running AI on a phone. It ended up giving me a reason to put that phone away.

    2 reactions
  5. Cleanup Walk: an Android app that turns screen time into a neighborhood cleanup. This is a submission for the Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass What I Built You tap "30-minute cleanup" (or 15, 45, 60), and the app plans a walking loop that routes you past known problem spots nearby. You pick up litter, snap photos of what you find, and at the end you get a summary like: > You cleaned 1.8 km of your neighborhood and removed 14 pieces of litter. The app's job isn't to keep you scrolling. It exists to get you out the door and hand you something useful to do once you're there. - Spots problems with the camera. Take a photo and an on-device model finds litter, overflowing bins, damaged public infrastructure, and fallen branches, with a count. You confirm or correct with one tap. - Plans a time-boxed route. Spots with damaged infrastructure and overflowing bins are weighted highest. Any spare time becomes exploration stops in a new direction, so you find new problems. - Guides the walk. It shows time left, distance walked, litter removed, and the direction to the next stop. A big +1 litter button counts everything you pick up along the way. - Remembers. Open issues are saved so future walks route past them, and lifetime totals show on the home screen. Who it's for: anyone who wants a reason to walk and leave the block a bit better than they found it. That includes people who care about their neighborhood but never know where to start. It works fully offline. There's no account, server, API key, or Google Play Services dependency, and photos never leave the phone. | Home | Walk in progress | AI detection | Summary | |:---:|:---:|:---:|:---:| | <img src="raw.githubusercontent.com" width="200"> | <img src="raw.githubusercontent.com" width="200"> | <img src="raw.githubusercontent.com" width="200"> | <img src="raw.githubusercontent.com" width="200"> | Code {% github JayanaGunaweera01/clean-up-walk %} How I Built It Open-source AI: Gemma 3n E2B. It's Google's open-weight multimodal model, running entirely on the phone through LiteRT-LM (Apache 2.0). Each scan sends the camera photo plus a fixed prompt, and Gemma replies in a strict, parseable format: litter: 3 overflowing_bin: 0 damaged_infrastructure: 1 fallen_branch: 0 main: litter confidence: high A few design decisions I'm happy with: - Forgiving parsing. Small models don't always follow the format perfectly. GemmaScan.kt holds the prompt and a parser that copes with markdown, odd casing, and missing lines, all covered by unit tests. - Deterministic output. Sampling is set to topK = 1, temperature = 0.1, and every scan uses a fresh conversation so photos don't influence each other. - Model stays loaded. The first scan loads the engine (a few seconds) and it's kept for the session. The vision encoder runs on the GPU when available and falls back to the CPU. - Swappable model. The .litertlm file is imported at runtime (⋮ → Import Gemma model), so you can try Gemma 3n E4B or a fine-tune without rebuilding. - AI suggests, human confirms. The model's answer comes first, and one tap confirms or corrects it. The route planner is plain Kotlin, not AI. Choosing spots within a time limit is an orienteering problem, so the planner: 1. Scores each issue: priority × (1 + ln(count)) × freshness, where freshness halves every 14 days. 2. Keeps only issues reachable and returnable within the budget. 3. Greedily inserts the best score-per-extra-second issue, improving the loop with 2-opt after each insertion. 4. Fills leftover time with an octagonal exploration loop heading away from visited issues. Stack: Kotlin, Jetpack Compose, Room, platform LocationManager, and LiteRT-LM. About 3,000 lines of Kotlin. Tests The core logic is pure Kotlin, so it's covered by unit tests that run in CI on every push to main: - Route planner: includes a randomized test that the estimated time never exceeds the time you chose - Gemma reply parser: markdown, odd casing, and missing lines - Walk manager and distance tracking: walks survive the app being closed - Domain model: scoring, freshness, and issue handling Passing CI run: ./gradlew testDebugUnitTest assembleDebug, which also builds the debug APK. ./gradlew testDebugUnitTest Why Does Open Innovation Matter? A cleanup app is only useful if people actually trust it with their camera and location, and that's where open weights changed the design. - Privacy by architecture. Because Gemma 3n runs on the device, photos are analyzed locally and deleted right after. There's no server to leak from and no upload policy to trust. - Works where the problems are. Litter and broken bins are often in places with poor signal. A closed vision API would have made the core feature useless exactly where it's needed. - No keys, no bills, no accounts. A free community tool can't carry per-photo API costs, and every user would need an account or key. With open weights the marginal cost of a scan is zero. - Swappable and improvable. The model is just a file. Anyone can drop in a bigger Gemma, or a version fine-tuned on their own city's litter and signage, with no rebuild. Prize Categories <!-- Match to the exact category names on the challenge page. --> - Gemma (Gemma 3n E2B running on-device via LiteRT-LM)

    2 reactions

Latest stargazers

The last 10 distinct accounts we observed this year.

Most active participants

PR, issue, comment & review events in the past year.

Packages

10
PackageVersionDownloads · 30dTrend · 30d
———
———
———
v1.0.2——
———
v0.2.0——
@litert-lm/corev0.19.0-nightly.20261024
v0.19.0-nightly.20261024
≥10.7K
———
———
———

README badges

Embed a badge in your README or website.

Live mentions · last 30 days
google-ai-edge/LiteRT-LM mentions in the last 30 days