Skip to content

Android: opt-in Partial Custom Tab (bottom-sheet) for the authorization flow #1118

@omeratt

Description

@omeratt

Problem

On iOS, the authorization flow opens as a modal sheet over the host app via
ASWebAuthenticationSession. The user never feels like they've left the app:
the host's wallpaper is visible behind the sheet, animations are continuous,
and dismissal returns control to the same screen.

On Android, the equivalent flow opens a Chrome Custom Tab in full-screen
mode
. The host activity is fully obscured. To users this reads as "the app
opened a browser" rather than "the app is asking me to sign in," which is
both a UX downgrade vs. iOS and a real cognitive interruption — especially
on the first sign-in / connect-account flow.

iOS (today, no library change) Android (today, no library change)
iOS ASWebAuthenticationSession bottom-sheet Full-screen Chrome Custom Tab — fully obscures the host app.

Chrome 107 (Oct 2022) shipped the Partial Custom Tabs API
that closes this gap: it renders the Custom Tab as a user-resizable
bottom sheet at a caller-specified initial height. With it, the Android
side can match iOS's modal feel:

Android with Partial Custom Tab (this proposal)
Android Partial Custom Tab bottom-sheet

The feature is opt-in (default behavior unchanged) and gracefully degrades:
older Chrome silently ignores the extra and falls back to the full-screen
Custom Tab — i.e. zero regression risk for users on Chrome < 107.

Proposal

Add one optional AuthConfiguration field, Android-only:

androidCustomTabPartialHeightFraction?: number; // (0, 1]

Semantics:

  • Unset (default) — no change. CustomTabsIntent.Builder is built as
    today; Chrome renders the full-screen Custom Tab.
  • Set to a fraction in (0, 1] (e.g. 0.85) — between
    authService.createCustomTabsIntentBuilder() and .build(), the library
    calls setInitialActivityHeightPx(displayHeight * fraction, CustomTabsIntent.ACTIVITY_HEIGHT_ADJUSTABLE). Chrome 107+ renders the
    bottom sheet; older Chrome ignores the extra and falls back to
    full-screen. The activity stays user-resizable to full screen via the
    drag handle (ACTIVITY_HEIGHT_ADJUSTABLE).
  • Skipped when androidTrustedWebActivity is true (TWAs intentionally
    render full-screen).

A fraction is preferred over raw pixels because it's display-agnostic — the
caller doesn't have to reach for Dimensions.get('window').height or worry
about dp vs. px. Chrome enforces a 50% floor internally, so values
below that are auto-clamped by Chrome (this is documented behavior, not a
library concern).

Implementation notes

  • androidx.browser bumps 1.4.0 → 1.5.0 for the
    setInitialActivityHeightPx API. The bump is API-compatible and 1.5.0
    has been stable since late 2022.
  • First-launch warmup caveat (worth documenting next to the new option):
    Chrome's Partial CCT requires the CustomTabsIntent.Builder to be
    constructed with an established CustomTabsSession to render as a
    bottom sheet on the first invocation. Otherwise Chrome's first launch
    falls back to full-screen and only subsequent launches get the sheet.
    The library already exposes
    prefetchConfiguration — calling
    it on the screen that will later invoke authorize() is sufficient
    (verified on a real Pixel emulator running Chrome 147). The PR's docs
    update mentions this explicitly.
  • No iOS counterpart needed — ASWebAuthenticationSession already
    presents as a modal sheet by design. The new option is silently ignored
    on iOS (guarded by Platform.OS === 'android').
  • No tests need mocking the native side — the JS layer's
    responsibility is validation + correct positional argument to the
    native bridge, both covered by spec additions.

Backward compatibility

  • Field is optional with default null (no behavior change when unset).
  • Existing API surface untouched: no field renamed, removed, or whose
    meaning shifted.
  • Existing tests pass after pinning the new positional arg.
  • Older Chrome / older Android versions: extra is ignored by Chrome, no
    crash, falls back to current full-screen Custom Tab behavior.

PR

A PR with the full implementation (TS types, JS validation, Java wiring,
README/docs, 5 new spec tests covering validation + bridge passthrough,
plus a changeset for the minor bump) is ready and will be linked below.
Happy to iterate on the API shape (e.g. accept an object form, expose
resizeBehavior, etc.) if maintainers prefer a different surface.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions