My parent lives alone. After a fall that nobody noticed for hours, I decided to build something that would.
Four months, 121 versions, and approximately 79,000 lines of Kotlin later, the app is live on Google Play. Here is the story — the technical challenges, the things that broke, and what I would do differently.
What the app does
Install it on your parent's Android phone. It watches. That is it.
For 7 days, it learns their routine — when they wake up, how active they are, where they go. After that, it monitors 24/7 and emails your family if something seems wrong:
- Unusual stillness (potential fall or medical event)
- Did not wake up on time
- At an unfamiliar location at an unusual hour
- Phone silent for too long
No buttons to press. No wearable to charge. No daily check-in calls. Install and forget.
The technical stack
- Kotlin + Jetpack Compose + Material Design 3
- Room + SQLCipher for encrypted local storage
- Google Gemini API for behavioral analysis (cloud, anonymized summaries)
- Resend API for transactional email alerts
- WorkManager + Foreground Service for 24/7 reliability
- Clean architecture:
:domain(pure Kotlin) ->:data->:ui->:app
## The hard part: staying alive on Android
This is where 80% of my development time went.
Android's job is to kill your app. OEMs make it worse. Here is what I learned:
### Problem 1: OEM battery killers
Samsung, Xiaomi, Honor, OPPO — they all have proprietary battery managers that kill background apps. The standard startForeground() is not enough.
My solution: 11-layer service recovery:
- Foreground service with IMPORTANCE_MIN channel
- WorkManager periodic watchdog
- AlarmManager backup chains
- BOOT_COMPLETED receiver
- SyncAdapter for process priority boost
- Batched accelerometer sensing (survives CPU sleep)
- Exact alarm permission recovery
- OEM-specific wakelock tag spoofing (Honor whitelists "LocationManagerService")
- START_STICKY restart
- Safety net AlarmClock at 8-hour intervals
- User-facing gap detection with OEM-specific guidance
Each layer was added because the previous ones were not enough on some device.
Problem 2: Sensor data lies to you
At 3 AM, your app wakes up to check the accelerometer. The sensor HAL returns data. You think it is fresh. It is not — it is 22-minute-old data sitting in the hardware FIFO buffer since the last time anyone read the sensor.
On Honor devices, the HAL even rebases event.timestamp on flush, so a delta check against elapsedRealtimeNanos() thinks the data is fresh. The solution: explicit sensorManager.flush(), discard warm-up readings, use onFlushCompleted() callback instead of fixed timers, and dual-clock comparison as a safety net.
### Problem 3: GPS does not work when you need it
getCurrentLocation(PRIORITY_HIGH_ACCURACY) returns nothing. The OEM killed the GPS hardware to save power.
Solution: Priority fallback chain — HIGH_ACCURACY -> wake probe -> BALANCED_POWER -> LOW_POWER -> getLastLocation(). Returns a GpsLocationOutcome sealed class so the caller knows exactly what happened.
## The AI: from on-device to cloud
I started with Gemini Nano (fully on-device). It worked on Pixels. It did not work on anything else. The addressable market was tiny.
So I moved to Gemini Flash (cloud API). The privacy trade-off: detailed behavioral data stays on-device in an encrypted database, but anonymized summaries (including location context) are sent to Google's AI for weekly analysis. No names, no personal identifiers.
The key architectural decision: API key sharding. Each Google Cloud project gets 10,000 requests per day free. I created 6 projects with independent API keys. The app rotates through them on rate-limit errors (429/403). That is 60,000 requests per day — enough for thousands of users at zero cost.
## Travel intelligence
The biggest UX win. Without it, every vacation generated 5 to 7 URGENT emails (one per night when the hard-floor detector fired at an unfamiliar location). By day 5, families ignored all emails.
Now: Day 1 sends one "your parent appears to be traveling" notification. Days 2 through 6: silence (unless something actually changes). Return home: "they have returned to a familiar area."
The state machine: HOME -> DAY_1 -> TRAVELING(n) -> TRIP_ENDED. Single-writer rule through TravelStateManager to prevent state corruption from concurrent assessments.
## What I would do differently
- Start with cloud AI from Day 1. I lost 2 months on Gemini Nano before accepting the device compatibility reality.
- Build the OEM compatibility layer first. The 11-layer recovery took 40+ versions to get right. It should have been the foundation, not an afterthought.
- Email before OAuth. I started with Gmail OAuth (user signs into their Google account). It was a UX nightmare. Resend API (transactional email, zero auth) took 1 day to implement and just works.
## Looking for early adopters
The app is free to download with a 21-day free trial (then $49 for the first year, $5 per year after that). I am looking for families to test it — install it on your parent's Android phone (Android 9 or newer), run it for a couple of weeks, and tell me what works and what does not.
Website: howareu.app
Top comments (11)
How does your app anonymize data?
Good question. The whole security topic is really sensitive. All data is stored in an encrypted database. When I send something to the AI for analysis, it is anonymous. In other words, I don't share who is the data owner. I send certain aggregated data of stillness periods, and some GPS locations. I even tried to use an internal LLM: Gemini Nano, but its limitation of max allowed tokens were a big hit for me. The data volume can vary from user to user, and I can't be sure that I can always fit in the Gemini Nano restrictions.
I would say about encryption that app can't send encrypted data outside If an external side isn't able to decrypt them. So, there is a logical point when data will be decrypted. Is it point inside your app or outside?
Thank you for the questions. It is always a pleasure to clear things out so that they are well understand. That's the whole idea behind such posts. Clear information to reach more people, and help them with their tasks and challenges.
Let's start from the beginning. The database is encrypted. Usually, this is done, so that the data is protected, if someone wants to steal it from the device directly. When the app uses the data, it needs to decrypt it first, so that it is able to query it, analyze it, aggregate it, what ever is needed. So decryption happens on the device.
Decrypted data never exits the application, but this is different from the database encryption. The communication with the AI, the Resend API is encrypted. It is dump to send plain text data to external systems, and https is the usual way to do such things.
In the database, I need some personal data, like emails, and also keeps information, like locations, and when they were visited. Information in that plain, and descriptive way never leaves the application. We send to Gemini AI,in encrypted way, only summaries. By writing above 'anonymized', it is meant that Gemini AI does not know (nobody knows) whose is the person who has those patterns of sleep, movements and stillness periods.
Is the main goal of encryption protect data on user's device only? Does your app have other goals for encryption?
Did I understand you correctly? Your app anonymize data and then encrypt them and save to the database, isn't it? Is anonymizing performed before encryption?
The API key sharding pattern across 6 GCP projects is one of those pragmatic architectural decisions that separates real production thinking from tutorial-level code. I deal with a similar scaling problem on a financial data platform — generating unique analysis content for 8,000+ stock tickers across 12 languages. Rate limits from data APIs were the single biggest bottleneck until I built a rotation and fallback layer that handles throttling gracefully instead of failing hard.
Your travel intelligence state machine is also brilliant UX thinking. The false positive problem you described (5-7 urgent emails per vacation) is basically the alert fatigue problem that kills every monitoring system. I ran into the same thing with automated site health checks — if every anomaly triggers an alert, people just stop reading them. The solution is always contextual suppression, exactly like your HOME -> TRAVELING state approach. Curious whether you considered adding a manual "traveling" override so the user can preemptively silence it, or if the automatic detection handles edge cases well enough?
The main idea is the app to be installed on parents' devices. And they usually don't like to be bothered. And usually they don't know what to do with their devices. One early adopter wrote that his parents are holding their devices like gradates :D That is why I want to follow "Install and forget" principles and don't bother elderly people with anything. Otherwise, they can find it intrusive and just delete it.
Having in mind the above, manual traveling from the device itself will not fit in the whole picture. Of course, curious parents can open it and see some nice looking statistics, but no actions are required for them to do. Actually, I expect that the installation itself will be done by their children.
But I am planning some other important features. If there is stillness for 24h, theapp will take all the available GPS coordinates for the last 24 hours and send email, so that the children can trace what their parent did. I hope that for people with dementia, this is draw a path and can create a predictability template for the children to know where to search.
Another feature that I like to include is SMS notifications. But, let's see how it will go and what the early adopters give back as feedback. :)
Thanks!
The sensor HAL rebasing timestamps on flush is such a nasty gotcha — most people assume sensor data is live and never question it. The dual-clock comparison as a safety net is smart.
Has your app been blocked by Google?
Not blocked, yet :) I am in closed testing phase. In order to publish it in production, I need early adopters (testers) to play with it for 14 days. Once this validation phase pass, I can start the publishing procedure. :)