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>
This commit is contained in:
Paul Lipscomb
2026-07-30 18:47:07 -05:00
parent f7ac34141a
commit 3425ec69de
909 changed files with 43638 additions and 0 deletions
+50
View File
@@ -0,0 +1,50 @@
# 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.