Skip to content

[Bug] Android: native server-picker pill overlaps the sidebar header controls and swallows their taps #3586

Description

@btli

Description

On the Android shell, the floating server-picker pill is a native TextView stacked on top of the WebView. At certain window widths it lands directly over the conversations sidebar's header controls — Search / Settings / Collapse sidebar — and, being a native clickable view above the WebView, it swallows every touch in its rect. Those three buttons become un-tappable.

Root cause. The pill is centred over the entire window, with no knowledge of the web layout beneath it:

web/android/app/src/main/java/ai/omnigent/android/MainActivity.kt:176-186
  FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT,
      Gravity.TOP or Gravity.CENTER_HORIZONTAL)

It is isClickable = true (:172), added to the FrameLayout after the WebView (:161, :187) so it is always on top, and its label is host[:port] (hostLabelOf, :460-474) with no width cap — so its width varies with the hostname.

Meanwhile the sidebar's action cluster is pinned to that card's top-right edge (web/src/shell/Sidebar.tsx:674-728, data-testid="sidebar-header-actions"): three size-6 buttons with gap-1, ≈80px total, inset 16px by px-4. The card is md:m-2 and user-resizable — default 320px, min 220, max 0.5 × window width (web/src/hooks/useResizableSidebar.ts:19-28).

With sidebar width S, window width W and half-pill-width p, the cluster spans x ∈ [S-88, S-8] and the pill spans x ∈ [W/2-p, W/2+p]. They collide iff:

W/2 - p + 8  <  S  <  W/2 + p + 88
  • Short host 10.0.2.2:8000 (p ≈ 50dp) at W = 768dp → collides for 342 < S < 906. The 320px default just misses, but any user-widened sidebar lands in it.
  • Long host omnigent.joyful.house (p ≈ 77dp) → collides for 315 < S < 933, so the default 320px sidebar collides immediately.
  • At the maximum sidebar width S = W/2, the cluster's right edge is at W/2 - 8, inside the pill for any p > 8 — it always collides there.

Vertically they always overlap (pill at y ∈ [safeTop+8, safeTop+~36]; the sidebar's 48px header row starts at safeTop thanks to the injected padding-top, putting the 24px buttons at y ∈ [safeTop+14, safeTop+38]). Horizontal position is the only variable — which is why this only bites when the screen width, sidebar width and hostname length line up.

A hide contract already exists on the web side, unimplemented on Android. web/src/lib/nativeBridge.ts:519-529 setNativeServerSwitcherHidden() calls native.setServerSwitcherHidden ?? native.setSidebarOpen, guarded by if (!setter) return;. The Android facade (NativeBridgeScript.kt:204-253) exposes only kind / setColorScheme / setBadgeCount / notify / onNotificationActivated / onNativeInsets, and the native dispatch table (OmnigentBridgeListener.kt:48) handles only the first three — so the call is a silent no-op on Android. Every web call site is isIOSShell()-gated (ChatPage.tsx:1585,1589; useNativeServerSwitcher.ts:17,86,90).

Note the existing iOS mechanism would not fix this case even if enabled on Android: it hides via an elementFromPoint hit-test at the chat surface's centre (useNativeServerSwitcher.ts:95-129), and at tablet widths the sidebar is a docked column that never covers that centre, so it would report "frontmost" and keep the pill shown.

Constraint worth preserving: MainActivity.kt:65-67 documents the pill as deliberately "Always visible so it's always available as a recovery path". Tapping it is the only entry point to showServerSwitcherMenu, so any fix that hides it must not strand a user whose server is unreachable — the sidebar is open by default at ≥768px (AppShell.tsx:1548-1551), so "hide whenever the rail is expanded" would hide the recovery control by default on every tablet.

iOS parity: web/ios/Omnigent/WebShellView.swift:32-45 centres the switcher the same way, but does cap its width at min(172, max(120, width*0.38)) (:197-201). Android has no cap.

Steps to reproduce

  1. Install the Android app on a tablet or unfolded foldable (or any device whose WebView viewport is ≥768 CSS px, so the sidebar docks instead of overlaying).
  2. Connect to a server with a reasonably long hostname (e.g. omnigent.joyful.house) — or use any host and drag the sidebar's right edge wider.
  3. With the conversations sidebar expanded, try to tap Search, Settings or Collapse sidebar in the sidebar's top-right corner.

Expected: the buttons respond.
Actual: taps landing under the server-picker pill are swallowed by the native view; the buttons are dead. Collapsing the sidebar (if you can still reach the control) or resizing it moves the collision away.

Version

4d23ed78 (0.8.0.dev0); Android shell versionName 0.1.0

OS

Android (tablet / unfolded foldable, e.g. Pixel 10 Pro Fold). The iOS shell shares the centred-overlay shape, mitigated there by a width cap.

Metadata

Metadata

Assignees

Labels

BugSomething isn't workingP2-mediumPriority: bug with workaround, important feature requestcomp:androidAndroid app shellcomp:web-uiComponent: web frontend (ap-web)triagedIssue has been triaged by the botvalidated:reproducedRepro agent validated the issue can be reproduced

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions