Skip to content

Android: chat input hidden behind keyboard #194

Closed

Description

@Cintao66

Beschrijving / Description

Op Android is de chat-invoerbalk (schrijfbox) niet zichtbaar wanneer het on-screen toetsenbord actief is. De invoerbalk schuift achter het toetsenbord. Pas wanneer het toetsenbord wordt verkleind (of gesloten) wordt de schrijfbox weer zichtbaar.

The chat input/composer is hidden behind the on-screen keyboard on Android. Only when the keyboard is shrunk or closed does the input box become visible again.

Reproductie / To Reproduce

  1. Installeer OpenCode Mobile op Android.
  2. Voeg een server-verbinding toe (via Tailscale, http://100.x.x.x:4096).
  3. Open een sessie en tik op het invoerveld.
  4. Het toetsenbord verschijnt en bedekt de schrijfbox; de box is niet te zien of aan te raken zonder het toetsenbord te verkleinen.

Verwacht gedrag / Expected behavior

Wanneer het Android-toetsenbord opent, moet de layout worden verkleind zodat de schrijfbox boven het toetsenbord zichtbaar blijft (vergelijkbaar met android:windowSoftInputMode="adjustResize" of correcte IME-inset-afhandeling). Dezelfde bug in de opencode web-UI is eerder gefixt in anomalyco/opencode#15842.

Omgeving / Environment

  • App: OpenCode Mobile (Android)
  • Server: opencode 1.18.18 (opencode serve, verbonden via Tailscale)
  • Toetsenbord: standaard Android-toetsenbord (o.a. Gboard)

Activity

  1. Qiiks commented on Aug 21, 2026

    @Qiiks

    Fixed by the merge of #182 into the Qiiks/opencode-mobile maintenance branch (maint/v0.4.16) — shipping in release v0.4.16-maint.1 (APK will be attached there). Upstream review of #182 is still recommended; this merge keeps the fork usable while it waits.

  2. duoyi88 commented on Aug 28, 2026

    @duoyi88

    Tested on a physical Pixel 11 Pro running Android 17. Play v0.4.15 reproduces this with both Gboard and a custom Trime IME. The keyboard change in #182 improves the layout but is still 56 dp short: the TextInput overlapped the IME by 106 px in the measured build.

    I opened focused follow-up PR #201, which offsets by the complete native-header height (insets.top + 56) and keeps the full composer above the IME in the patched device build.

  3. duoyi88 commented on Aug 28, 2026

    @duoyi88

    Follow-up on #201: physical testing caught one more regression in the first offset-based revision — after dismissing the IME, React Native 0.81.5 retained stale bottom padding and the composer did not return to the bottom.

    PR #201 has now been updated to use Android keyboardDidShow / keyboardDidHide metrics directly: show applies the reported IME height; hide resets padding to zero. The rebuilt side-by-side APK passed the complete open/close cycle on the Pixel 11 Pro: full composer visible above the keyboard, then restored to the bottom after dismissal.

  4. vstandos commented on Sep 5, 2026

    @vstandos

    Can confirm this reproduces consistently when connecting to a self-hosted opencode serve — the composer stays fully behind the soft keyboard while typing. Thanks for looking into it, looking forward to #193/#201 landing. Much appreciated!

  5. omgoshjosh commented on Sep 21, 2026

    @omgoshjosh

    Tracking the broader Android IME regression here rather than opening a duplicate.

    Authoritative Android evidence covers two keyboard-visible states: (1) a multi-choice question card whose lower choices cannot be usefully scrolled above the IME, and (2) a long multiline composer whose editable viewport/caret becomes unusably constrained. The contribution will preserve recoverable question navigation, use keyboard-aware bounded composer growth with internal scrolling, and cover resize/orientation, accessibility scaling, dismissal, and iOS non-regression.

    Reserved clean contribution branch: fix/android-ime-scroll-composer-upstream, based on upstream main at 646f9cbf7743e08115c8121d98c82f48d088a8f4. No implementation, release, or merge has occurred. Device reproduction, root-cause evidence, tests, and independent QA/design review will be attached before a PR is requested.

  6. omgoshjosh commented on Sep 21, 2026

    @omgoshjosh

    Implementation is available in draft PR #208.

    Passed: npm test (340), npm run typecheck, npm run check:versions; independent code and QA/design review completed.

    The physical Pixel 8 Pro post-change install/smoke is deferred: the capacity broker denied the Android build while an active 10 GB iOS build lane holds capacity. No bypass was attempted. Existing reproduction evidence remains at /var/folders/fc/xccpf99d62b5_sc13zpgvjd80000gn/T/opencode/mobile-ime-repro-20260920/.

  7. omgoshjosh commented on Sep 21, 2026

    @omgoshjosh

    Physical Pixel candidate smoke result for PR #208: BLOCKED by test environment connectivity. Candidate commit 95b001d loaded on Pixel 8 Pro 38141FDJG00BG7 at 1008x2244; bundle markers and repository gates verified. The installed dev client persisted 127.0.0.1:12400 and could not reach a session; therefore authoritative question-choice and multiline-composer screenshots were not honestly claimable. Evidence: file:///var/folders/fc/xccpf99d62b5_sc13zpgvjd80000gn/T/opencode/mobile-ime-repro-20260920/candidate/. No feature code changed; device/Metro/lease cleanup completed.

  8. omgoshjosh commented on Sep 21, 2026

    @omgoshjosh

    QA PASS: validated PR #208 at commit 95b001d on physical Pixel 8 Pro 38141FDJG00BG7. Recreated the persisted http://127.0.0.1:12400 endpoint with a question-aware REST/SSE mock and adb reverse; candidate JS loaded through Metro (bundle evidence: /private/var/folders/fc/xccpf99d62b5_sc13zpgvjd80000gn/T/opencode/mobile-ime-repro-20260920/candidate/android.bundle.js; markers include question-options-scroll, COMPOSER_MIN_HEIGHT=88, composerMaxHeight, keyboardVerticalOffset).\n\nAuthoritative captures: /Users/josh/agents/media/mobile-ime-qa-95b001d-20260921/. Screenshots/XML cover question card before/after IME scroll, lower choices visible above IME, pinned Dismiss/Next footer; multiline draft with multiple lines/caret, internal scroll and edit; keyboard dismissal, send/attachments smoke captures; 1.5x font and compact resize. Screenshots are 1008x2244 physical pixels; app/window class is 763x1536 at 360dpi, keyboard-visible app bounds recorded in XML/window-displays.txt. Tested question options scroll, footer navigation, composer focus/multiline/edit, keyboard dismissal, send path, attachments control, running/stop-capable mock path, compact resize, default/1.5x scale, safe-area bounds, portrait lock.\n\nCleanup PASS: device lease released (broker leases=0), 12400/8081/13000 reverse mappings removed, owned mock and Metro stopped, port claims released, font_scale restored to 1.0, wm size reset, rotation settings unchanged. No product files edited.

  9. omgoshjosh commented on Sep 21, 2026

    @omgoshjosh

    PR #208 is ready for maintainer review at 95b001d after final bug-focused review PASS. Pixel 8 Pro QA validated scrollable keyboard-visible choices, pinned navigation, and a usable multiline internal composer viewport: /Users/josh/agents/media/mobile-ime-qa-95b001d-20260921/. Gates: 340 tests, typecheck, version parity. Design PASS; residual risk is one Pixel/IME combination. No merge performed.

  10. dzianisv commented on Nov 13, 2026

    @dzianisv
    Owner

    Claiming this as the owning lead — Ember, OpenCode Mobile task agent.

    Task: fix the Android keyboard hiding the chat input (#194, #156). Branch: fix/keyboard-ime-194. Scope: Android composer + connection-form IME handling only — evaluate open PRs #193/#201/#206/#208 (and #203), merge the best one or land an equivalent fix. No applicationId or dependency changes.

    Work Log row: "Fix Android keyboard hiding chat input (#194, #156) — merge one of PRs #193/#201/#206/#208".

  11. dzianisv commented on Nov 13, 2026

    @dzianisv
    Owner

    Keyboard IME fix — context pack (maintained, DONE, 2026-11-13)

    Stage: DONE — merged. Lead: Ember (OpenCode Mobile task agent). Branch: fix/keyboard-ime-194 (head 484e227d305ae76bfaca1be94f2d34a4965ed81d, from main e8444931). Work Log: "Fix Android keyboard hiding chat input (#194, #156) — merge one of PRs #193/#201/#206/#208" (Done). No design/PRD links exist for this fix.

    Scope / acceptance: Android composer + connection forms. (a) composer visible above keyboard on open; (b) usable multiline typing (caret/viewport, internal scroll); (c) returns to bottom on dismiss, no stale padding; (d) connection add/edit usable with keyboard; (e) no iOS regression (padding, offset 90).

    Stage 1 decisions: All six candidates OPEN + mergeable=false / dirty, stale bases, no CI checks reported on any branch — merge none as-is. Re-implement from scratch: #201 live-IME-height mechanism (base) + #208 bounded multiline composer + scrollable QuestionPrompt; reject #206 (stale v0.4.16 release commit), #203 new dep (owner call), #193 adjustPan (connection part already landed via #216), #182/#208 static insets.top offset alone (contradicted by #201 Pixel 11 Pro measurement: 56 dp / 106 px short). #182 bundle and #190 (closed 2026-08-21, superseded by #182) not merged for this task; #182 non-keyboard parts belong to separate tasks (#155, #186/#189). Branch fix/android-keyboard-covers-input (24fe947f, fixes #53, pre-edge-to-edge) is superseded, no open PR.

    Design gap (bug-lane record): No IME-inset handling ever existed as a first-class layer. The app stacked native adjustResize and JS KeyboardAvoidingView with no contract about which coordinate space either reasons in. Under Expo mandatory edge-to-edge the window origin moved while KAV mixed window-frame coords with screen-space keyboard coords, so every fix that only changed the behavior prop (#53/#70/#74/#147/#148) re-tuned the symptom. The native insets plugin (#210/#216) then fixed only API 35+, with no JS-side coordination — Android ≤14 broken, 15+ exposed to double-application.

    Stage 3 — PR #236: fix(android): keep chat composer above the keyboard (#194, #156), head 484e227d, 6 files +217/−14. app/session/[id].tsx (listeners tracking IME height + screenY, JS paddingBottom, behavior={undefined} on Android, composer maxHeight + scrollEnabled); QuestionPrompt.tsx (scrollable body, pinned footer — byte-identical to #208 for this file); src/lib/keyboard-offset.ts + test (from #201, + API-35 guard helper); src/lib/session-layout.ts + test (from #208, composer cap + question scroll props only). No new dependencies; applicationId untouched; manifest/gradle/package.json untouched. Gates on exact head SHA: check:versions exit 0, typecheck exit 0, full suite 395 pass / 0 fail (baseline clean main: 380). Test-count delta: +15 (380 → 395), test files 48 → 50. Only ONE test dropped (the static insets.top Android offset — measured 56 dp short on Pixel 11 Pro); the iOS-offset test moved to keyboard-offset.test.ts. No mock-based tests.

    API-35 decision (Stage 3): JS padding gated — JS owns API < 35, native plugin owns API 35+. jsKeyboardPaddingApplies(platform, apiLevel) returns true only for Android with numeric Platform.Version < 35, so exactly one layer applies IME height on every API level. Composer cap + QuestionPrompt scroll stay active on all API levels. iOS unchanged. Connection screens need no change (KAV-wrapped via #216, covers (d)).

    Stage 4 — PASS (verdict 6855569318, lead-read-back): Full 345-line diff read; AC (a)–(e) all met in code; API-35 guard verified exactly-one-layer; test delta re-verified by reviewer own runs; no scope creep; sweep re-run zero unfixed hits in scope. Notes (not failures): app/(tabs)/index.tsx New Session + Rename modals still use KAV behavior="height" on Android; connection forms on API <35 still rely on KAV behavior="padding" offset 0 (mitigated by scrollable forms). claude-code-review skill file does not exist on this VM — method applied from the brief; install before the next review. CUA is NOT a required check; checkout AGENTS.md "MANDATORY CUA before merge" text is stale (workflow deleted on Den order, no credits).

    Stage 5 — MERGED (lead, 2026-11-13): CI green on head 484e227d — Typecheck and unit tests pass, test pass, build pass (23m47s). Merge commit 2667ad5c9efebc9a65f5237673d0ea2d649d7399 (main HEAD, verified by readback). Issues #194 and #156 auto-closed by the merge. Merge comment 6856070558 on #194. Superseded PRs closed with comments: #193, #201, #206, #208, #203. #182 left open — its non-keyboard parts belong to separate tasks (#155, #186/#189).

    Stage 5.5 — Design/PRD sync: checked — no update needed. No design or PRD doc exists for this fix, and no standing design page describes IME/keyboard behavior (hub and runbooks describe process only). Recorded in merge comment 6856070558.

    Open residual: Physical-device proof (open / multiline / dismiss on a real Android phone, API <35 and 35+) was not run — no Android SDK/device on this VM. Deferred to release validation: owner phone check on the v0.4.18 build before it ships. No release in this task.

  12. added a commit that references this issue on Nov 13, 2026
  13. dzianisv commented on Nov 13, 2026

    @dzianisv
    Owner

    Merged — PR #236 (Stage 5, 2026-11-13)

    Fix landed on main: PR #236 "fix(android): keep chat composer above the keyboard (#194, #156)", merge commit 2667ad5c9efebc9a65f5237673d0ea2d649d7399 (main HEAD, verified by readback). Issues #194 and #156 auto-closed by the merge.

    What shipped: #201 live-IME-height mechanism (keyboard listeners apply the reported IME height as padding, explicit reset on hide) + #208 bounded multiline composer (cap, internal scroll) + scrollable QuestionPrompt with pinned footer. API-35 guard: JS padding owns API < 35, the native insets plugin owns 35+, so exactly one layer applies IME height. iOS unchanged. No new dependencies; applicationId untouched.

    Evidence: Stage 4 review PASS (comment 6855569318, head 484e227d); CI green on the head — Typecheck and unit tests, test, build (23m47s). Full suite 395 pass / 0 fail (baseline 380), test files 48 → 50.

    Stage 5.5 Design/PRD sync: checked — no update needed. No design or PRD doc exists for this fix, and no standing design page describes IME/keyboard behavior (hub and runbooks describe process only).

    Open residual: physical-device proof (open / multiline / dismiss on a real Android phone, API <35 and 35+) was not run — no Android SDK/device on this VM. Deferred to release validation: owner phone check on the v0.4.18 build before it ships. No release in this task.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions