From f570bdefb738bafad6a6f7983508419411935c1d Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Thu, 22 Oct 2026 07:41:05 +0200 Subject: [PATCH] fix(alarms): keep the upcoming notice off a locked screen below Android 12 The Dismiss action's unlock requirement is honoured only from Android 12, so on 10 and 11 the notice is now secret on the secure lock screen rather than dismissible by whoever holds the phone. Fixes #16 --- .../clockula/alarm/ring/UpcomingNotifications.kt | 16 +++++++++++++--- docs/ARCHITECTURE.md | 3 ++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/de/jeanlucmakiola/clockula/alarm/ring/UpcomingNotifications.kt b/app/src/main/java/de/jeanlucmakiola/clockula/alarm/ring/UpcomingNotifications.kt index c4f182b..92e0c9b 100644 --- a/app/src/main/java/de/jeanlucmakiola/clockula/alarm/ring/UpcomingNotifications.kt +++ b/app/src/main/java/de/jeanlucmakiola/clockula/alarm/ring/UpcomingNotifications.kt @@ -6,6 +6,7 @@ import android.app.NotificationManager import android.app.PendingIntent import android.content.Context import android.content.Intent +import android.os.Build import androidx.core.app.NotificationCompat import de.jeanlucmakiola.clockula.MainActivity import de.jeanlucmakiola.clockula.R @@ -18,8 +19,9 @@ import de.jeanlucmakiola.clockula.domain.alarm.UpcomingNotice * (#16). Silent and low-importance: it is a way out for someone already awake, * not a second alarm. Its Dismiss is the engine's `dismissUpcomingOccurrence` * for the one occurrence it names, so it can never end a ring that has already - * started; it asks for an unlock first, so a locked phone cannot be talked out - * of its alarm by whoever picks it up. + * started; it asks for an unlock first (and below Android 12, where it cannot, + * the notice is kept off the secure lock screen), so a locked phone cannot be + * talked out of its alarm by whoever picks it up. */ object UpcomingNotifications { @@ -56,7 +58,15 @@ object UpcomingNotifications { .setContentText(text) .setCategory(NotificationCompat.CATEGORY_REMINDER) .setPriority(NotificationCompat.PRIORITY_LOW) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + // The Dismiss asks for an unlock from Android 12; below that the + // platform cannot, so there the notice stays off a locked screen. + .setVisibility( + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + NotificationCompat.VISIBILITY_PUBLIC + } else { + NotificationCompat.VISIBILITY_SECRET + }, + ) .setOnlyAlertOnce(true) .setSilent(true) .setShowWhen(false) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index cfda3ee..c3bd2a0 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -1260,7 +1260,8 @@ the alarm's row for whoever opens the app instead. proves you are awake while an alarm sounds; before it, skipping is already one unchallenged switch away in the editor. The notification's Dismiss does ask for an **unlock** (`setAuthenticationRequired`), so a locked phone cannot be - talked out of its alarm by whoever picks it up. + talked out of its alarm by whoever picks it up. That flag needs Android 12; + below it the notice is `VISIBILITY_SECRET`, kept off the secure lock screen. - **Denied notifications** cost the notice and nothing else; the row's chip still works. A swipe is recorded as an `id@fireAt` key in a small SharedPreferences file through the notification's delete intent, which fires