Skip to content
All posts

Android 16 Live Updates: putting Mintly's focus timer on the lock screen

A practical guide to Android 16's progress-centric notifications — ProgressStyle segments, promoted ongoing notifications, and what they add to a running Pomodoro timer.

MFKAPPS 4 min read

A running timer that only lives inside the app is a timer nobody checks. The whole point of a Pomodoro session is that you stop watching it — you glance at the lock screen when you surface, see how much focus time is left, and go back to work. Android 16 gives ongoing notifications a real answer for that glance: Live Updates, a promoted notification type built around ProgressStyle that earns a spot on the lock screen and a compact chip in the status bar instead of getting buried in the shade. Here’s what it actually does, and how it changed the notification Mintly shows during a session.

What a Live Update adds over a normal ongoing notification

An ongoing, non-dismissible notification with a progress bar has worked on Android for years — that’s what powered Mintly’s timer notification before this. What it didn’t get was priority placement. A normal ongoing notification sits in the shade next to everything else, and on a phone with a busy notification list, it’s one row among many.

A Live Update is a Notification.Builder call away from the same content, but the system treats it differently: it’s eligible to show on the lock screen even when other notifications are collapsed, and on supported devices it can render as a small live chip near the status bar clock. The system decides whether to actually grant that promoted placement — requesting it doesn’t guarantee it — but for the notification types Android built this for (a ride approaching, a delivery en route, a timer counting down) it’s the difference between “the user has to unlock and pull down the shade” and “the user reads it off the lock screen without touching anything.”

Wiring ProgressStyle into a timer notification

The style itself is a small addition on top of a notification you’re already building:

val progressStyle = NotificationCompat.ProgressStyle()
    .setProgress(elapsedSeconds)
    .setProgressMax(totalSeconds)
    .setProgressIndeterminate(false)

val notification = NotificationCompat.Builder(context, CHANNEL_FOCUS_SESSION)
    .setContentTitle("Focus session")
    .setContentText(formatRemaining(totalSeconds - elapsedSeconds))
    .setSmallIcon(R.drawable.ic_mintly_timer)
    .setOngoing(true)
    .setOnlyAlertOnce(true)
    .setStyle(progressStyle)
    .setRequestPromotedOngoing(true)
    .build()

The one setting worth pausing on is setRequestPromotedOngoing(true). It’s a request, not a switch — the platform can decline it, and it’s meant for notifications that genuinely represent something the user is actively waiting on right now, not a general-purpose way to jump the queue. A timer that’s actually running qualifies. A stale notification left over from a session that ended ten minutes ago doesn’t, and the system’s own heuristics will start ignoring promotion requests from an app that asks for it when nothing is really ongoing.

Segments turn a bar into an actual session map

The part that made this worth adopting for Mintly wasn’t the lock-screen placement on its own — it was ProgressStyle’s support for segments. Instead of one continuous bar, you can describe the progress as a sequence of colored ranges, which maps directly onto how a Pomodoro session is actually structured: work block, short break, work block, longer break.

val segments = listOf(
    ProgressStyle.Segment(25 * 60).setColor(mintlyFocusColor),
    ProgressStyle.Segment(5 * 60).setColor(mintlyBreakColor),
    ProgressStyle.Segment(25 * 60).setColor(mintlyFocusColor),
    ProgressStyle.Segment(15 * 60).setColor(mintlyLongBreakColor),
)

progressStyle.setProgressSegments(segments)

That single change answers a question the old flat progress bar never could: not just “how much time is left in this session,” but “where in the cycle am I, and how far to the next real break.” Points are the other primitive ProgressStyle exposes — small markers along the bar — and they’re a natural fit for interruption checkpoints, though Mintly doesn’t use them yet.

The failure mode: treating this as decoration

It’s tempting to bolt ProgressStyle and the promotion request onto every ongoing notification an app has, since the API surface is small and the visual upgrade is real. That’s the wrong instinct. Live Updates exist for a narrow category — something time-bound and actively in progress that the user cares about right now — and the platform is watching for apps that request promotion outside that category. An app that promotes a “you haven’t opened this in 3 days” nudge, dressed up as a progress bar, is exactly the abuse case this feature was scoped to prevent, and repeated bad requests are the kind of thing that gets an app’s future promotion requests quietly deprioritized.

The honest test before adding setRequestPromotedOngoing(true) anywhere: is there a real, running process behind this notification, ending at a specific time the user can predict from the bar itself? For a focus timer, yes on every count. For most other notification types in a typical local-first app, no — and the plain notification you already have is the right one to keep.

What this doesn’t replace

Live Updates changes where the timer notification is seen. It doesn’t change whether it fires reliably in the first place — that’s still the foreground service and wall-clock end-time math Mintly already relies on to survive Doze and process death. ProgressStyle and the promotion request sit on top of that foundation; they’re a presentation layer, not a substitute for getting the underlying timer correct. Build the reliable version first. The lock-screen chip is worth adding once that’s not in question, not before.