Skip to content

fix: permanent island disappearing after notifications/unlock (#237)#243

Open
noelpatata2 wants to merge 1 commit into
D4vidDf:masterfrom
noelpatata2:fix/permanent-island-zombie-notifications
Open

fix: permanent island disappearing after notifications/unlock (#237)#243
noelpatata2 wants to merge 1 commit into
D4vidDf:masterfrom
noelpatata2:fix/permanent-island-zombie-notifications

Conversation

@noelpatata2

Copy link
Copy Markdown

What this fixes

Fixes #237 (permanent island getting removed after receiving notifications and never coming back). Likely also improves #227.

Root cause (confirmed on a real device)

Debugged on-device (Redmi Note 15 Pro 5G, HyperOS, global ROM) with logcat + dumpsys notification:

The island swipe gesture is an expand/collapse toggle handled entirely by systemui. It never cancels the underlying focus notification and no onNotificationRemoved callback ever fires. After the user swipes an island away, its bridged notification stays posted (ONGOING_EVENT, focusType=PARAMS) as an invisible zombie — verified in dumpsys minutes after the swipe, with zero removal events in the app's logs.

Consequences:

  • activeIslands never drops back to 0, so PermanentIslandManager.updateState() keeps the permanent island suppressed forever.
  • Zombies accumulate with every swiped island. removeOriginalNotification mode is hit worst, because syncNotifications() deliberately exempts those islands from the stuck-notification cleanup (their original key is expected to be missing).
  • Secondary issues compounded it: PermanentIslandManager state is mutated from three different threads with no synchronization, and a cancel→post pair issued in the same instant can be swallowed by HyperOS, leaving notification 9999 posted but its island hidden.

The fix, in layers

  1. Thread safety + reconciliation (PermanentIslandManager): all state access serialized (@Synchronized/monitor); on listener connect, screen-on and unlock the cached isIslandActive belief is reconciled against whether ID 9999 is actually posted, re-asserting the island in place when needed.
  2. Debounced dispatch: the permanent island is posted ~700 ms after the desired-state transition instead of instantly, so HyperOS doesn't swallow a rapid cancel→post.
  3. Coexistence (the key UX change): the permanent island is no longer removed while bridged islands are active. HyperOS shows the newest focus island on top, so when a bridged island collapses or expires, the permanent island is revealed instantly — previously there was a gap (or with the zombie bug, it never returned).
  4. Zombie bounding: MESSAGE/STANDARD islands get a 60 s TTL (exact same mechanism live updates already used), and the periodic sync reaps own-package notifications that are no longer tracked (e.g. leftovers after a service restart).
  5. Battery: the 1-minute periodic sync is skipped while the screen is off; SCREEN_ON already triggers a full refresh sync on wake, so nothing is lost.

Also included (happy to split into a separate PR and/or gate behind a setting — say the word): a "watch relay" — when removeOriginalNotification cancels the original, companion apps that mirror notifications to smartwatches (Mi Fitness etc.) lose the relay. This posts a silent, 10-second, low-importance mirror they can forward. Tested with a Redmi Watch 5: watch vibrates again while the phone shows only the island.

Testing

All verified on-device (Redmi Note 15 Pro 5G, HyperOS, v0.5.5 base):

  • Permanent island survives: notification arrival → dismiss/open, repeated; lock/unlock cycles; island swipe-collapse.
  • Bridged islands render and behave normally on top of the posted permanent island (no double-pill artifacts observed).
  • Zombie notifications from before the fix are cleaned on service start.
  • Watch relay: Redmi Watch 5 vibrates with remove-original enabled.

Not tested: CNROM, other device models, widget islands (code paths untouched), non-Shizuku mode.

Full disclosure

This fix was developed AI-assisted (Claude Code / Anthropic) — "vibecoded", as the kids say — driving a real device over adb: the diagnosis came from logcat/dumpsys evidence, and every layer was tested on-device by a human before landing here. Review with whatever extra skepticism that deserves; happy to iterate on anything.

🤖 Generated with Claude Code

…f#237)

Root cause (confirmed on-device, HyperOS / Redmi Note 15 Pro 5G): the
island swipe gesture is an expand/collapse toggle handled entirely in
systemui - it never cancels the underlying focus notification and no
listener callback fires. Bridged notifications therefore outlive their
islands as invisible "zombies" (worst with removeOriginalNotification,
whose islands are exempted from the stuck-notification sync cleanup),
so activeIslands never drops back to 0 and the permanent island can
never return.

Fixes, in layers:

- PermanentIslandManager: serialize all state access (it was mutated
  from main/Default/IO with no synchronization); debounce dispatch by
  700ms so a cancel->post pair is not swallowed by HyperOS; reconcile
  cached state against the actually-posted notification on listener
  connect / screen on / unlock, re-asserting the island in place.
- Keep the permanent island posted while bridged islands are active:
  HyperOS shows the newest focus island on top, so the permanent island
  reappears instantly when a bridged island collapses instead of
  leaving a gap (it previously stayed gone forever).
- Give MESSAGE/STANDARD islands a 60s TTL (same mechanism live updates
  already use) and reap own-package notifications that are no longer
  tracked (e.g. after a service restart), so zombies are bounded.
- Skip the 1-minute periodic sync while the screen is off; SCREEN_ON
  already triggers a full refresh sync on wake.

Also adds an optional "watch relay": when the original notification is
removed, post a silent short-lived mirror so companion apps (Mi
Fitness etc.) can still forward it to a smartwatch. Happy to split
this into its own PR or gate it behind a setting if preferred.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.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.

[BUG] Permanent Island getting removed after receiving notifications

1 participant