v0.3.1 introduced TouchInterceptor (a FrameLayout wrapping the
ComposeView) so we could intercept touches before the inner MapView
saw ACTION_DOWN. That made the wrapper the window-root view (the
View actually attached to WindowManager) but I left the
ViewTreeLifecycleOwner / ViewTreeSavedStateRegistryOwner tags on
the inner ComposeView.
Compose's WindowRecomposer.create looks up findViewTreeLifecycleOwner
on the window-root, not on the inner ComposeView. With the tag
missing on the wrapper, Compose throws IllegalStateException at
composition startup — service crash on overlay enable.
Fix: move setViewTreeLifecycleOwner + setViewTreeSavedStateRegistryOwner
to the wrapper. v0.3.0 worked because the ComposeView itself was the
window-root then; the same pattern (owners on whatever's attached
directly to WindowManager) holds here.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Bug: dragging the bubble panned the OSM map instead of moving the
bubble. The OnTouchListener was attached to the ComposeView, but
the inner MapView consumed ACTION_DOWN for its own pan handling
before the listener fired. Fix: wrap the ComposeView in a custom
TouchInterceptor FrameLayout whose onInterceptTouchEvent always
returns true. Touches go to the wrapper's OnTouchListener; child
views (including MapView) never see them. Bubble is now purely a
visualization — pan/zoom is impossible.
- Drag-to-dismiss: when the user starts dragging (after passing
TAP_SLOP), a translucent dark circle with a white X appears at
bottom-center via a separate WindowManager view. Highlights red
when the bubble's screen-space center is within hit slop of the X.
Releasing on the X tears down the bubble AND fires onDismissed —
DetectionService flips setOverlayEnabled(false) so the toggle and
the bubble state stay in sync. Releasing elsewhere is a normal
drag (just repositions). The dismiss zone uses FLAG_NOT_TOUCHABLE
so it never steals the gesture; it's purely visual feedback.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 23:45:01 -04:00
3 changed files with 188 additions and 45 deletions
* Compose's [ComposeView] requires both a [LifecycleOwner] and a
* [SavedStateRegistryOwner] in its view tree. A bare [android.app.Service]
* isn't an SSR owner, so we synthesize one bound to the bubble's lifetime.
* The lifecycle is forced to RESUMED on construction (Compose only renders
* at STARTED+) and DESTROYED on [destroy].
* Compose's [ComposeView] requires both a LifecycleOwner and a
* [SavedStateRegistryOwner] in its view tree. A bare Service isn't an SSR
* owner, so we synthesize one bound to the bubble's lifetime. The
* lifecycle is forced to RESUMED on construction (Compose only renders at
* STARTED+) and DESTROYED on [destroy].
*/
privateclassOverlayOwner:SavedStateRegistryOwner{
privatevallifecycleReg=LifecycleRegistry(this)
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.