Architecting Battery-Efficient Geofencing for Automated Sound Profiles
It was the middle of a Friday afternoon prayer session at the local masjid. The room was heavy with silence, a collective stillness that felt almost fragile. Suddenly, the sharp, upbeat jingle of a popular pop song blasted from somewhere in the back row. A hundred heads turned toward the culprit, who was frantically fumbling with their device, face turning bright red as they tried to swipe away…
On a busy Friday afternoon, a group of worshippers sat in a local masjid in contemplative silence. Suddenly, a loud pop song started playing from somewhere in the back row, causing everyone to turn and point at the culprit. This scenario is all too familiar for many of us who have been the source of an unwanted interruption in a setting where silence is expected, whether it's during a medical appointment, a classroom, or a job interview.
Existing solutions to this problem either required manual toggling of sound settings or relied on intrusive automation apps that were more complex than necessary.
The author realized that there was a need for a simple yet effective solution that could automatically adjust the phone's sound settings based on the environment. The technology existed for GPS and scheduling APIs, but the challenge was to create a reliable system that would not drain the battery. The key hurdle was the Geofencing API, which needed to detect when a user entered a specific area and change the phone's sound profile accordingly.
The GeofencingClient in Android is designed to be efficient by offloading monitoring to the system, using a combination of network location and low-power GPS signals to determine proximity. However, the system can kill background tasks or throttle them to save resources if too much processing is done within the BroadcastReceiver handling the geofencing transition.
To address this, the author combined the GeofencingClient with a ForegroundService. The GeofencingClient acts as the low-power monitor, sending intents to the BroadcastReceiver when a geofence transition occurs. This BroadcastReceiver then triggers a JobIntentService or a WorkManager task to change the sound profile.
A crucial aspect of this architecture is separating the detection of geofence transitions from the action of changing the sound profile. The author stored sound state settings in a local Room database instead of volatile variables to ensure that even if the app process was killed by the system to free up memory, the service could still wake up, query the database, and reapply the correct AudioManager settings.
The author used AudioManager.setRingerMode() to set the phone to silent or vibrate mode, but also had to handle the NotificationManager separately for Do Not Disturb mode due to restrictions on newer Android versions. The complexity wasn't just in the code; it was in managing the state machine. The author had to implement a priority-based queue for multiple triggers, ensuring that if two geofences overlapped, the one with higher priority would take effect.
Testing across different OEM implementations also revealed that relying solely on standard Android behavior wasn't enough; manufacturers like Samsung had aggressive battery management settings that could interfere with the service.
One of the most significant learning experiences was the restoration phase of the app. The author initially assumed that simply setting the phone to silent when entering a location and then reverting to normal when leaving would be sufficient. However, this logic breaks if a user enters multiple overlapping geofenced areas. The app had to be designed to keep track of all active routines, only returning the phone to normal when the stack of active geofences was empty.
Additionally, the author found that using AlarmManager for time-based triggers was unreliable due to the Doze mode in newer Android versions. Instead, the author used setExactAndAllowWhileIdle to ensure that triggers like prayer times were accurate, even if the device had been idle for hours.
The author also recognized the importance of accurately calculating prayer times, which involves local time offsets that can vary significantly between regions. Initially, the app relied on GPS coordinates for these calculations, but the author found that additional user-defined time offsets were necessary. This required refactoring the entire calculation engine to support these offsets, a task that became quite challenging after the initial release.
Looking back, the author would have invested more time in handling the Adhan library and time-zone offsets earlier in the development process. They would also have opted for DataStore instead of SharedPreferences from the start, as SharedPreferences can block the UI thread during disk I/O, leading to minor stutters. This migration, while significant, proved to be a crucial step in improving the app's performance.
Through building Muffle, the author learned that the most effective background services are those that do the least. The goal isn't simply to stay alive; it's to be woken up precisely at the right moment to perform a single task. This lesson was invaluable in creating a battery-efficient geofencing solution that respects the user's environment without sacrificing battery life.
Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.