Skip to content

Fix notifications before the targetSdk bump (channel, PendingIntent mutability, POST_NOTIFICATIONS)#10

Open
xroche wants to merge 2 commits into
masterfrom
notifications-modern
Open

Fix notifications before the targetSdk bump (channel, PendingIntent mutability, POST_NOTIFICATIONS)#10
xroche wants to merge 2 commits into
masterfrom
notifications-modern

Conversation

@xroche

@xroche xroche commented Jul 17, 2026

Copy link
Copy Markdown
Owner

Three things that break notifications at targetSdk 36, landing ahead of the bump so that the bump itself stays a one-variable change.

The PendingIntent is the one that matters: a bare 0 flag throws from API 31 on, and one of the three callers is RunnerFragment.onDestroy, so this is a crash on every aborted crawl rather than a lost notification. Lint has been flagging it as UnspecifiedImmutableFlag all along, unnoticed because abortOnError is false. The other two are silent by nature: a channel-less notification is dropped from API 26 with only a log line, and POST_NOTIFICATIONS needs an explicit request, since targeting 33+ removes the automatic prompt.

Everything lands in sendSystemNotification(), the single sink for every notification the app posts. (showNotification() is a Toast despite the name, so its call sites are untouched.) The permission is requested at crawl start rather than at launch, once per install and latched in preferences: the activity has no configChanges, so a rotation would otherwise re-enter the progress pane and re-ask, and the second refusal is the one that sticks for good. The channel is registered at post time for the same reason, since creating a channel is what makes the system prompt an app targeting 32 or lower, and because the save-failure notification can fire without any crawl having run.

This is not inert on current devices, contrary to what the first commit assumed: androidx branches on the device SDK_INT and never on targetSdk, so on any API 26+ device the notifications now carry a real channel and the app gains an entry in the system notification settings. Master is in fact silently broken on Android 13 today, creating no channel and therefore never getting the system's own permission dialog, so a fresh install posts nothing at all. This PR is what fixes that.

Verified by differential: lint reports UnspecifiedImmutableFlag once on master and zero here, and POST_NOTIFICATIONS appears in the merged manifest of the built APK with targetSdkVersion still 25. The channel and the prompt cannot be exercised without a device, so they rest on the API contract rather than on observation.

Left alone deliberately, each wanting its own PR: the abort PendingIntent reuses request code 0 without FLAG_UPDATE_CURRENT, so a second abort notification carries the first one's extras and tapping it restores the wrong project (pre-existing, master behaves identically), and the mirror-finished intent carries no extras at all, which is what its FIXME says.

xroche and others added 2 commits July 17, 2026 10:27
Three separate breakages in sendSystemNotification(), the single sink for
every notification the app posts. All three are inert at targetSdk 25, so
this lands now and the target bump stays a one-variable change.

The PendingIntent is the severe one: a bare 0 flag throws from API 31 on,
and one of the three callers is RunnerFragment.onDestroy, so the app would
crash on every aborted crawl rather than merely lose a notification. The
extras are read back only by us, so FLAG_IMMUTABLE is the right one. Lint
has been reporting this as UnspecifiedImmutableFlag all along; it went
unnoticed because abortOnError is false.

The channel is mandatory from API 26 on, and without it the system drops
the notification with nothing but a log line. NotificationChannelCompat
registers it with no version gate.

POST_NOTIFICATIONS is enforced by the OS from Android 13, but targeting 33+
also removes the automatic prompt, so an explicit request is the only way
to get anything at all. It is asked at crawl start rather than at launch
because two refusals make it permanent, and a prompt at launch has nothing
to point at yet.

setContentInfo() went with the rewrite: it has been ignored since API 24.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
…t time

Review of the first commit found the request could fire twice. The activity
has no configChanges, so a rotation mid-crawl restores pane_id, re-enters
the progress pane and re-asks; a user who declined once and reflexively
declines the repeat has denied POST_NOTIFICATIONS for good. A field would
die with the instance like pane_id does, so the latch lives in the existing
preferences file.

Creating the channel in onCreate was also wrong in a subtler way. androidx
branches on the device's SDK_INT, never on targetSdk, so the channel really
was created on every launch of every API 26+ device -- and for an app
targeting 32 or lower, creating a channel is precisely what makes the system
show its own permission dialog. Startup is the worst moment to ask, being
the one point where there is no mirror to announce. Registering it in
sendSystemNotification() instead keeps it ahead of every post, including the
save-failure one that can fire without any crawl.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Signed-off-by: Xavier Roche <roche@httrack.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant