Skip to content

feat(screensaver): add real-time interactive photo drag & direction-aware slide transition - #166

Open
miguelsg29 wants to merge 17 commits into
starbrightlab:mainfrom
miguelsg29:feature/screensaver-swipe-transition
Open

feat(screensaver): add real-time interactive photo drag & direction-aware slide transition#166
miguelsg29 wants to merge 17 commits into
starbrightlab:mainfrom
miguelsg29:feature/screensaver-swipe-transition

Conversation

@miguelsg29

Copy link
Copy Markdown

Summary of Changes

This PR introduces real-time interactive touch gesture navigation and direction-aware slide transitions to the screensaver (PhotoFrameController):

1. Interactive 1:1 Touch Drag Navigation (ACTION_MOVE)

  • Real-Time Drag Tracking: As the user drags horizontally across the screensaver, the current photo and adjacent photo move in 1:1 real-time sync with finger movement.
  • Zero-Gap Contiguous Layout: Positioned incoming adjacent photos edge-to-edge with the current photo, eliminating black gaps during drag interactions (carousel / filmstrip effect).
  • Smooth Release Momentum: Releasing the drag smoothly continues the slide from the exact release position to completion using DecelerateInterpolator, or snaps back cleanly if released under threshold.

2. Background Adjacent Photo Preloading (LruCache)

  • Instant Swipe Performance: Integrated an in-memory LruCache<String, Bitmap> for preloading adjacent (next & previous) photos on a background thread pool immediately after any photo displays.
  • Zero Latency: Eliminates disk and network decoding delays during swipe gestures.

3. Preserved Auto-Dwell Transition

  • Automatically changing photos over time continues to use the standard 900ms crossfade alpha transition.

Verification & Unit Tests

  • Passes all unit tests: ./gradlew :app:testDebugUnitTest (BUILD SUCCESSFUL).
  • Verified real-time gesture dragging, zero-gap carousel transitions, and instant preloading on physical Meta Portal Go hardware (./gradlew :app:assembleDebug).

@starbrightlab starbrightlab left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this — the gesture/animation work is genuinely nicely built (the slide-completion math resuming from the live drag translationX and the snap-back reset are clean). But there are two blocking issues that would bite on real Portal hardware, so requesting changes.

Blockers

1. Main-thread I/O in the touch handler → crash. setupAdjacentDragBitmap() is called from onTouch ACTION_MOVE (main thread), and on a cache miss it falls back to a synchronous decode/fetch (PhotoFrameController.kt:246,251). For remote sources that's fetchRemoteImage on the UI thread → NetworkOnMainThreadException → launcher crash (and a crash on a home app clears the HOME preferred-activity association). It fires on the first swipe of any neighbor whose preload hasn't landed. The fallback must run on the io executor with the layer wired up in the posted ui continuation — never inline in ACTION_MOVE.

2. LruCache(8) is entry-count sized → OOM. LruCache<String,Bitmap>(8) (PhotoFrameController.kt:214) has no sizeOf override, so it's 8 entries regardless of pixel size. A 12MP original at inSampleSize=1 ≈ 48 MB ARGB_8888 × 8 ≈ ~300 MB retained, on top of the live layers + blur copies. That's a guaranteed OOM/heavy-GC regression on the RAM-limited Portal. Size the cache in bytes as a fraction of Runtime.maxMemory() (override sizeOfbitmap.byteCount) and cap to prev/current/next (2–3 entries).

Should-fix

  1. Dwell timer races the dragACTION_DOWN/MOVE never cancel the auto-advance tick, so it can flip activeLayerIndex / reset translationX mid-drag and move the wrong layer. Suspend the tick on ACTION_DOWN/while dragging, reschedule on UP/snap-back.
  2. ACTION_CANCEL can trigger dismiss — UP and CANCEL share a branch (:300); a CANCEL with small dx/dy falls through to the tap test and calls onExit. Gate onExit to ACTION_UP only.
  3. Black gap on incomplete preload — if the incoming bitmap is null, the code still sets incomingLayer translation and the UP slide animates a blank/stale layer in. Guard the commit unless a real incoming bitmap was set.

Nits: no multi-touch/pointer-id handling; the index math + slide-duration calc are pure and worth a small unit test.

Happy to help once these are addressed — the core UX is worth landing.

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.

2 participants