All posts
Engineering6 min read

How the Android app rings on time without asking the server

A reminder that arrives through the server needs the server to be awake at the right minute, a push service to deliver it and the phone to be reachable. For a task at 3 PM, that is three things that can be late. The Android app does it the other way: it plans its own alarms from what it already has, and the server's one daily message becomes a backup, not the bell.

The phone already knows

The screens read your tasks, reminders, events, trips, projects and people into a local cache so the app works offline. Everything a bell needs is in there: a title, a time and whether it is done. So each time those lists change, and each time the app comes to the front, a planner reads them and works out every alert that should ring.

  • A task with a time rings at that time, and once more two hours later if nothing happened to it: no Mark done, no Snooze, no tap.
  • A list of the day's all-day tasks at 9 AM.
  • Each reminder and calendar event at its time; an all-day event at 9 AM.
  • A trip 3 days and 1 day before it starts, and on its first morning.
  • Someone past their catch-up cadence at 6 PM, once per cadence.
  • A project on its deadline's morning, and as it passes 25, 50, 75 and 100% done.
  • A streak at its milestones, and at 8 PM on a day with nothing done yet.

Re-planning without ringing twice

Planning from scratch on every change is simple and safe, but cancelling every alarm and setting it again is not: an alarm due this minute could be cancelled just before it fires. So every alert carries a stable id and a fingerprint of what it says and when. A run cancels only the alarms the new plan no longer has, sets only the ones that are new or changed, and leaves the rest alone. Move a task and it never rings at its old time; leave it alone and its alarm is never touched.

One-off notices, like a project crossing 50% or a streak reaching seven days, are remembered on the phone so each is said once, however many times the plan runs.

Exact, or a few minutes late

Android lets an app ask for an alarm at an exact minute only with the Alarms & reminders permission. Without it, the system may batch the alarm with others to save battery, and in Doze that can mean minutes. In our own test on an emulator in Doze, an exact alarm fired 13 milliseconds after its time and an inexact one 46 seconds late. The app uses exact alarms when the permission is there and says so when it is not.

The reminder check

Four things decide whether an alarm rings: notifications allowed, battery optimisation off, exact alarms allowed, and the phone maker's own battery manager, which closes background apps on top of Android's rules and which no app can read. Settings has a Reminder check that shows the first three, gives the steps for your phone's maker for the fourth, and has a Send a test reminder button: it posts a reminder a minute ahead through the same path as every task alarm, and you say whether it rang. That one test covers all four.

When it shipped

  1. 2.1.16: the local alerts for timed tasks, trips, streaks, catch-ups and project milestones, which had been written and never switched on.
  2. 2.1.19: alarms that may fire while the phone is idle, and the 9 AM list of the day's all-day tasks.
  3. 2.1.20: exact alarms when allowed, the two-hour follow-up, and the Reminder check.
  4. 2.1.36: reminders, calendar events and project deadlines ring on the phone too.

Each kind has its own switch in the app's Notifications settings, kept on that phone, and the account's Push Notifications switch turns them all off.

Sources

The modules this touches

By situation

Try it on the free plan

No card. Both workspaces, unlimited tasks, the Planner, the Calendar and Focus.