Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 0.1.8 - 2026-06-01

- Android: complete the Samsung One UI Recents-cover fix from v0.1.7. The instance-based `exclude2 = coverContent` exclusion in `topmostHostViewFor` only filtered the *current* cover content; on Samsung One UI (Galaxy A05 / SM-A057F, Android 14, One UI 6/7), rapid `setColor` / `setImage` / `enable` JS calls during `LockCover` mount trigger 2-3 sequential attach cycles (`ensurePreMounted` → `refreshCoverContentIfMounted`), and the previous attach's `FreezableFrameLayout` lingers briefly in `WindowManagerGlobal.mViews` after teardown. The walk picked the stale instance as "topmost", `addCover` tried to attach as a sub-window of an invalidated token, and `WindowManager.addView` threw `BadTokenException: Unable to add window — token … is not valid; is your activity running?` on the third attach in the burst. The result: cover never appeared in the Recents thumbnail. Fix: add a class-based filter in `CoverWindowAttachment.topmostHostViewFor` that rejects any `FreezableFrameLayout` regardless of instance, so stale entries from previous attach cycles can't be mistaken for the topmost host. `FreezableFrameLayout` was made `internal` (package-visible) to keep the type reachable from `CoverWindowAttachment`. The instance-match `exclude2 = coverContent` is preserved as a fast-path. On every non-Samsung device the SCVH-hosted content never appears in `WindowManagerGlobal.mViews` to begin with, so the new filter is a no-op. Verified end-to-end on Samsung Galaxy A05 (cover paints in Recents with `#5F8AFA` background and splash icon, no `BadTokenException`), Pixel_9 emulator (9/9 maestro flows green, same baseline as v0.1.6), and Favvy_Android_30 / API 30 (5× home/app-switcher + 10× rapid toggle stress, zero crashes, API 30 `INTERNAL_SYSTEM_WINDOW` pre-check still fires).

## 0.1.7 - 2026-06-01

- Android: fix the cover never appearing on Samsung One UI (observed on Samsung Galaxy A05 / SM-A057F, Android 14). The host-window walk in `ensureCoverOnTopmost` and `addCover` excluded the cover Window's root (`coverView`, a `SurfaceView` on the SCVH path) but not the SCVH-hosted content (`coverContent`, a `FreezableFrameLayout`). On Samsung One UI's customised WindowManager the SCVH content appears in `WindowManagerGlobal.mViews` despite living inside the SCVH's own `ViewRootImpl`, so the walk identified our own content view as the topmost host, tried to attach the cover as a sub-window of itself, and `WindowManager.addView` threw `BadTokenException: Unable to add window — token … is not valid; is your activity running?`. The cover never reached the Recents thumbnail on those devices. Fix: pass `exclude2 = coverContent` to `CoverWindowAttachment.topmostHostViewFor` at all three callsites so the walk skips both views and falls back to the activity decor. `CoverWindowAttachment` already supported the parameter (the blur source picker uses it for the same reason). Verified on Samsung Galaxy A05 (Android 14, One UI) — Recents thumbnail now shows the configured `#5F8AFA` background with the splash icon, exactly the iOS App Switcher parity it was designed for.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,20 @@ internal object CoverWindowAttachment {
val v = views[i]
if (v === exclude) continue
if (v === exclude2) continue
// Reject ANY cover-owned view, current or stale. The instance
// matches above only filter the live `coverView` / `coverContent`
// we last set; previous attaches whose teardown raced with a
// re-attach can leave stale `FreezableFrameLayout` instances
// briefly lingering in `mViews` on some OEM WindowManager
// builds (notably Samsung One UI on Android 14 — observed on
// Galaxy A05 / SM-A057F). Without this class-based filter the
// walk picks one of those stale views as "topmost", tries to
// attach the new cover as a sub-window of its now-invalid token,
// and `addView` throws `BadTokenException: Unable to add window
// — token … is not valid; is your activity running?`. The cover
// never reaches the Recents thumbnail. Class match catches all
// instances regardless of which attach cycle created them.
if (v is FreezableFrameLayout) continue
if (v.windowToken == null) continue
// Reject views that have been detached from their ViewRootImpl
// but are still lingering in mViews (mDyingViews entries — see
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ private data class ImageConfig(
/// The freeze is one-way per view instance: once a cover content view
/// is being torn down, it's discarded. `attachCover` always builds a
/// fresh container, so a future show() starts from `frozen = false`.
private class FreezableFrameLayout(context: Context) : FrameLayout(context) {
internal class FreezableFrameLayout(context: Context) : FrameLayout(context) {
@Volatile var frozen: Boolean = false

override fun requestLayout() {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-cover",
"version": "0.1.7",
"version": "0.1.8",
"description": "🔒 Native privacy cover for React Native. Hides your app behind an overlay in the iOS App Switcher and Android Recents screen.",
"main": "./lib/commonjs/index.js",
"module": "./lib/module/index.js",
Expand Down
Loading