Files
virtual-controller/remote-client-android/IOS_PARITY.md
T
Paul Lipscomb 3425ec69de Android tablet remote UI: styling pass, color picker, arrange mode indicator, connection gating
- Button styling standardization with green ring state indicators (Connect, Rescan, Log, Clear Layout, Reset ALL)
- Input field labels with proper DP-based padding and spacing
- Feedback & title widget color picker with 7-color palette and persistence layer
- Font weight selection for text widgets (deferred visual implementation)
- Hide/Show and Reset Size buttons in context menu
- Flashing pink border around entire app when Arrange mode is ON (500ms fade in/out cycle)
- Arrange button disabled until connected to desktop app, with alpha indicator
- WidgetLayoutState now persists color and fontWeight properties
- Arrange mode visual debugging with smooth fade animation instead of harsh strobe

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-07-30 18:47:07 -05:00

6.5 KiB
Raw Blame History

iOS Feature Parity Tracker

Tracks what remote-client-ios (source of truth) actually does vs. what remote-client-android has built, so gaps get found by checking this list against the iOS source, not by surprise mid-conversation.

Status: Matched · ⚠️ Partial/Different (deliberate or otherwise) · Not built · iOS behavior not yet verified against source

Top button row

  • Single horizontal row, natural (not stretched) size, iOS's chained trailing/leading order
  • tabletIdField — iOS has a live editable field; Android hardcodes TABLET_DEST_ID = 1
  • autoSwitchButton — auto preset-switching toggle, not built
  • gridButton — visual grid-overlay toggle, not built (no grid overlay exists at all)
  • cancelChangesButton — revert unsaved Arrange-mode edits, not built
  • Preset-switch confirmation alert (isLocked==false && isNewPreset → "Save & Switch"/"Discard & Switch") — Android just always re-renders silently

Networking / protocol

  • WebSocket connect/dispatch, Bonjour discovery, control/control_f sends
  • ⚠️ save_layout — iOS never sends this over the wire, it's 100% local (UserDefaults) only. Android sends save_layout to the desktop on every drag/resize. Known, deliberate-at-the-time divergence, not yet reconciled.
  • widget_visibility on Hide — iOS sends {"event":"widget_visibility","uid":...,"dest_id":0} on hide, telling the desktop to un-route that widget (unchecks its remote checkbox there). Android's Hide is local-only, desktop has no idea.

Widgets — regular (preset-routed)

  • ⚠️ FaderWidget — confirmed against source: iOS's regular fader has the same modeButton (REL/ABS) and 90dp top/bottom padHeight dead zone as FocusFaderWidget, both fit inside the same 540dp total (not additive) — ported to Android's FaderWidgetView (now FrameLayout-based, mirroring FocusFaderWidgetView's structure). Android's total height is 540+180=720dp (not iOS's literal 540) to keep the active drag range unshrunk — deliberate, pre-existing divergence, not a bug. Android's regular fader also got a FINE button, which iOS's regular FaderWidget does NOT have (FINE is Focus-only on iOS) — added by direct request, an intentional Android-only addition beyond parity.
  • ToggleWidget/TransportWidget — 75×75dp, matched
  • FeedbackWidget — 220×100dp, matched. (Verified 2026-07-29: this is really "matched by both being unused" — FeedbackWidget.swift exists on iOS but is never instantiated, and Android has no regular-feedback view or model. The 220×100 constant only ever applied to the Focus feedback widget.)
  • TitleWidget — confirmed against source: iOS hardcodes a single uid = "title-big" instance too (idx in defaultFrame(idx:) is unused for title — leftover grid-placement signature shared with faders/toggles/feedbacks). Android's one-title-per-preset binding matches.

Widgets — Channel Focus

  • FocusFaderWidget — REL/ABS + FINE buttons, dead-zone touch math
  • Focus Fader length rescale context-menu options ("50% Longer" / "70% Longer" / "Reset Length") — height-only, bottom-edge anchored, multiplier applied to default (not current) height.
  • FocusToggleWidget — momentary mode + Gray/White non-darkenable color quirk
  • FocusFeedbackWidget — chrome-less (transparent background, no resting card border, no caption view, 8dp padding), 32sp monospace semibold at #EBEBEB, single line. Box is a pure function of (font size, text) via fittedSize, re-fit center-anchored on every widget_feedback. Pinch drives font size, not the box; "Reset size" restores 32sp; min 12sp. Font size is not persisted — it's back-derived from the saved height on load, matching iOS exactly (see "Networking" note: no schema change). Two knowing approximations: iOS's .semibold (weight 600) is only expressible from API 28, so API 2427 falls back to BOLD; and Android still flashes the activity ring green on incoming widget_feedback where iOS's applyFeedbackText doesn't flash at all.
    • Perf note (2026-07-29): re-fitting calls setLayoutParams(), which on Android requestLayout()s up the tree — unlike iOS's bounds.size, which costs siblings nothing. This was briefly suspected of causing fader stutter and reverted; the stutter turned out to persist without this code, so it was reinstated. The unproven suspect is instead the faders' own setLayerType(LAYER_TYPE_SOFTWARE) plus per-onDraw LinearGradient allocation. If this widget ever does need optimizing, the cheap fix is skipping the re-fit when the newly measured size equals the current one — monospace means most readout updates measure identical.
  • ⚠️ LogWidget — iOS is a draggable, pinch-resizable floating console with In/Out/Local category tabs, only rebuilding its TextView when the visible category matches what's being logged (the actual reason it doesn't have Android's perf problem). Android's log panel is a fixed bottom overlay, single combined stream, no categories, no drag/resize.

Context menu (long-press, Arrange mode only)

  • Align Left/Center/Right (TextAlignableWidget) — not built
  • Text Color (TextColorableWidget, from the named palette matching desktop's palette.py) — not built, local-only styling on iOS (no wire event)
  • ⚠️ Hide — present on Android but wrong semantics (see "Networking" above: no widget_visibility send)
  • Reset Size — box dimensions for regular widgets, font size for FocusFeedbackWidget (matching iOS, which means two different things by the same menu item).
  • Focus Fader length rescale (see above) — remote-client-android/.../arrange/{ArrangeCardView,ArrangeUILayout,ArrangeFunctionsAlerts}.java

Arrange mode / layout

  • Free 2D drag, pinch-resize (generic, top-left anchored)
  • Per-preset + Channel Focus separate layout persistence
  • Clear Layout (per-mode) + Reset ALL Layouts (Android-only addition, flagged as an iOS backlog item in memory)
  • ⚠️ Default placement: iOS uses defaultFrame(idx:) per-type grids at fixed screen-relative coordinates (with real overlap between types by iOS's own design). Android stacks everything at (0,0) — simpler, deliberately chosen over Android's earlier over-engineered fit-to-screen algorithm.

When picking something off this list to build: re-read the actual iOS source file first (don't assume from this doc alone — it's a snapshot, not guaranteed current), confirm the items against source before treating them as gaps.