Skip to content

sample: streaming-app player, iOS 27.1 target, and corner occlusion fix - #3

Open
bmc08gt wants to merge 4 commits into
mainfrom
feat/sample-player-ui
Open

bmc08gt wants to merge 4 commits into
mainfrom
feat/sample-player-ui

Conversation

@bmc08gt

@bmc08gt bmc08gt commented Oct 1, 2026 •

Copy link
Copy Markdown

Four commits on top of main.

Corner occlusion padding (hinge-compose). On the iPhone Duo the camera housing is a corner region ([585,0,669,120] on a 669×951 window). occlusionInsets() treated any region touching the right edge as right-only, so it padded the whole right edge by 84pt and left an empty strip down the Notes and Player screens. A region touching two edges now pads only the cheaper one, and rememberOcclusionPadding() takes a reserved PaddingValues: a region that padding already clears adds nothing, otherwise the result covers only the remainder. The sample passes its content padding, whose top bar clears the housing. OcclusionInsetsTest covers both corner cases.

Player screen. Replaces the emoji controls and plain queue with a streaming-app layout: an overlay on the video that hides while playing, an Up next list with thumbnails and progress, and episode details with a speed control. Book puts the player and details on one side of the fold and Up next on the other; Tabletop puts the video on the raised half and the controls on the flat half; single-pane windows scroll everything in one column. Icons live in PlayerIcons as path data.

iOS 27.1 deployment target. The sample now targets the first release with the Duo APIs, so DuoHingeSource.swift drops its #available checks and the Any? workaround.

iOS bridge fixes. DuoHingeSource now retries attaching the hinge interaction and rotation observer on didBecomeActive, so observing before the window exists no longer leaves the posture on Unknown. A half-open hinge with no active division region now takes its axis from the window's shape (tall reads Tabletop, wide reads Book); the old fallback always picked Book, though the Duo's tall window is portrait with a horizontal fold.

:hinge-core:allTests, :hinge-compose:iosSimulatorArm64Test and :sample:android-app:assembleDebug pass locally, and the iOS app builds against the Xcode 27.1 SDK.

Brandon McAnsh added 3 commits October 1, 2026 13:52
The iOS sample targeted iOS 18, so DuoHingeSource gated every Duo type
behind #available(iOS 27.1, *) and passed UIHinge around as Any? to keep
it out of ungated call sites. No system before 27.1 has a hinge to report,
so the gates only cost readability. With the deployment target at 27.1 the
source uses UIHinge directly.

README and CLAUDE.md now describe the 27.1 target. The README keeps the
#available advice for apps that copy the file and support older systems.
The first player used emoji glyphs for transport buttons and a plain
queue, so it showed the pane shapes but didn't look like a player
anyone ships. It now follows the usual streaming-app pattern: a video
stage with an overlay (minimise, captions, more, seek ±10 s, scrubber,
full screen) that hides itself while playing, an "Up next" queue with
thumbnails, durations and per-episode progress, and episode details
with a speed chip.

The fold still decides the arrangement. Book or a wide window puts the
video and details beside the queue, Tabletop moves the scrubber,
transport controls, speed and captions to the flat half, and a single
pane stacks video, details and queue in one list.

PlayerIcons holds the handful of Material icons as path data, because
the icons artifact is no longer published for Compose Multiplatform.

Checked on the Pixel Fold and Pixel 10 emulators and on the iPhone Duo
simulator in Book, Open, rotated Flat, Tabletop and Closed.
On the iPhone Duo, the camera housing is a corner region ([585,0,669,120]
on a 669x951 window). occlusionInsets() treated any region touching the
right edge as right-only, so the housing padded the whole right edge by
84pt and left an empty strip down the Notes and Player screens.

A region touching two edges now pads only the cheaper one.
rememberOcclusionPadding() takes a reserved PaddingValues: a region that
padding already clears adds nothing, and otherwise the result covers only
the remainder, so it stacks with the caller's own padding. The sample
passes its content padding, whose top bar clears the housing.
@bmc08gt bmc08gt self-assigned this Oct 1, 2026
…ndow

DuoHingeSource attached its hinge interaction and rotation observer once,
in begin(). If there was no key window yet, both were skipped and nothing
tried again, so an app that started observing before its window existed
stayed on Unknown with no error. The didBecomeActive observer now also
attaches whatever is still missing. Checked on the Duo by skipping the
first attach: the activation retry attached both and the hinge handler
published the half-open status.

derivePosture fell back to Book for a half-open hinge with no active
division region, on the claim that the Duo's hinge is vertical in
portrait. The rotation logs show the opposite: the tall 669x951 window is
portrait and its fold is horizontal. The fallback now takes the axis from
the window's shape, so a tall window reads Tabletop and a wide one Book.
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