DEV Community

Haseeb
Haseeb

Posted on

Architecting Offline Geofencing Without Battery Drain: The Muffle Journey

It happened during a quiet afternoon at the local library. I was deep in a focused debugging session when my phone suddenly blared a loud, jarring ringtone. Every head in the room turned toward me. My face burned with immediate, intense embarrassment. I had forgotten to silence my device after leaving a noisy coffee shop. That moment wasn't just an annoyance; it was the catalyst for realizing how much cognitive load we dedicate to simply managing our phone's sound settings. I knew there had to be a way to automate this without sacrificing battery life.

We all live these moments. You walk into a lecture, a medical appointment, or a mosque for prayer, and the silence is shattered by a notification sound. You reach for your phone, fumble with the volume buttons, and hope you didn't disturb anyone too badly. Then, you walk out, get busy, and leave your phone on silent for the rest of the day, missing important calls from family or work. The existing solutions were either too heavy, requiring constant cloud connectivity, or they were unreliable, failing to trigger when you actually arrived at a location. I wanted a tool that functioned strictly on-device, respecting privacy while being invisible to the system performance.

When I started building Muffle, I knew the Geofencing API provided by com.google.android.gms.location would be the primary engine for location-based triggers. However, the standard implementation is notoriously aggressive. If you simply register a broad proximity alert without tuning, your device wakes up the GPS radio constantly, leading to rapid power depletion. My initial approach was to use a simple GeofencingRequest with a Geofence.GEOFENCE_TRANSITION_ENTER and EXIT trigger, but I quickly realized that the OS often delays these triggers to preserve battery, causing the phone to stay loud for several minutes after entering a silent zone. This defeated the purpose entirely.

To solve this, I moved away from relying solely on high-accuracy GPS. Instead, I implemented a hybrid approach using PRIORITY_BALANCED_POWER_ACCURACY. By setting the setLoiteringDelay parameter to a specific threshold, I could filter out momentary signal noise that would otherwise trigger false positives when walking past a building. I also had to manage the PendingIntent carefully to ensure it didn't keep the process alive longer than necessary. Here is a snippet of the triggering logic I eventually landed on for the GeofencingRequest builder:

kotlin
val geofence = Geofence.Builder()
.setRequestId(routineId)
.setCircularRegion(lat, lng, radiusMeters)
.setExpirationDuration(Geofence.NEVER_EXPIRE)
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
.setLoiteringDelay(30000)
.setNotificationResponsiveness(60000)
.build()

This architecture forces the system to balance accuracy with energy efficiency. By allowing a 60-second responsiveness window, I offload the heavy lifting to the Google Play Services location hardware abstraction layer rather than polling the location myself. This keeps Muffle as a dormant background service that only wakes when the hardware signals a transition. It handles the AudioManager changes directly in a BroadcastReceiver, ensuring the transition happens within a few hundred milliseconds of the OS triggering the geofence event. It’s not about fighting the OS; it’s about speaking its language of efficiency.

What surprised me most during development was the fragility of the Android ForegroundService lifecycle in relation to OEM-specific battery management. I assumed that if I registered my receivers and services correctly, the OS would respect them. I was wrong. On devices from manufacturers like Xiaomi or Samsung, my background tasks were being killed almost immediately after the user cleared the app from the recent tasks list. I spent nearly two weeks debugging why my geofences would stop working after a phone reboot. It turned out that simply declaring a BOOT_COMPLETED receiver wasn't enough; I had to implement a persistent WorkManager task to verify the geofence registration status every time the device restarted.

I also learned that GPS coordinates aren't as static as we think. Using a fixed point for a geofence in a dense urban area often leads to 'bouncing' where the device toggles between entering and exiting the zone due to GPS drift. I initially tried to fix this with a simple timer, but that was insufficient. I ended up adding an 'overlap buffer' in my logic—a secondary validation check that ensures the device has maintained a consistent state for at least 30 seconds before committing to a sound profile change. If I were starting over, I would build a much more robust abstraction layer for the location data source, allowing me to switch from GPS to Wi-Fi triangulation if the signal accuracy drops below a certain threshold. The lesson here is that software is only as good as the hardware sensors it relies on, and those sensors are rarely 100% reliable in real-world conditions.

For any developer working on automation apps, the most important takeaway is to minimize the amount of logic running in your foreground service. Keep it strictly as a pass-through. If you need to perform heavy calculations, like computing prayer times based on complex coordinate offsets, do that inside a Worker class managed by WorkManager. By offloading these tasks, you ensure that your app remains responsive even when the system is under heavy load. The goal is to be a background citizen that the OS wants to keep alive, not one it wants to prune.

Automation shouldn't be complicated to manage. Muffle is my attempt to solve that friction by keeping everything local, offline, and silent. If you want to see how this handles different scenarios, you can explore the implementation details at https://play.google.com/store/apps/details?id=com.muffle.app. Building this taught me that the best features are the ones you set up once and never have to touch again. Focus on the user's peace of mind, and the technical architecture will follow.

Top comments (0)