Skip to content

fix(mobile): keep Android composer above keyboard after app switches - #17102

Closed
artile wants to merge 1 commit into
pingdotgg:mainfrom
tao-io:fix/android-composer-app-resume
Closed

artile wants to merge 1 commit into
pingdotgg:mainfrom
tao-io:fix/android-composer-app-resume

Conversation

@artile

@artile artile commented Oct 8, 2026 •

Copy link
Copy Markdown

Returning to an Android thread with its keyboard still open disables the composer's keyboard translation. The field drops behind the keyboard until it is dismissed and opened again.

Keep the translation enabled when an owned input retains focus across the app switch. Preserve the stale-keyboard guard after blur, including sending and backgrounding during keyboard dismissal. Move the guard into a hook so its resume behavior can be tested directly. This is a focused fix for an existing layout bug.

Closes #17087.

Validation:

  • The app-switch regression test failed with the old guard and passes with the fix.
  • vp test run apps/mobile/src/features/threads/use-keyboard-resume-guard.test.tsx apps/mobile/src/features/threads/pendingUserInputLayout.test.ts: 9 passed. Covers retained focus, blur before resume, refocus, keyboard height/visibility updates, and iOS.
  • vp run --filter @t3tools/mobile typecheck: passed.
  • Targeted lint: no errors. The existing keyboard-state reset effect and existing ThreadDetailScreen code report React warnings.
  • vp run lint:mobile: SwiftLint passed; Kotlin lint tools are not installed. No native code changed.
  • Built and installed T3 Code Dev on a separate Android 17 emulator (Google APIs, arm64, Pixel 6 profile). Used a copied, stopped-thread database with neutral test text.
  • Native pass: focused the existing-thread composer, entered an unsent K, switched to Settings search with its keyboard open, and returned to T3. With the fix, the field stayed above the keyboard and retained K. Native keyboard status confirmed that the keyboard remained visible. Sending a provider turn was not tested in the native pass.
  • A baseline screenshot with the original resume condition showed the missing composer. Before/after screenshots and the fixed-flow recording are saved locally in the T3 thread. They are not uploaded to GitHub; PR work uses gh as requested. This PR remains a draft pending attachment of that UI evidence.

Test setup needed a temporary Boolean-to-1/0 conversion in the pairing query to get past an unrelated SQLite binding failure on the test server. That server change was restored after testing and is not included. The test server, Metro, and emulator were stopped; the branch is clean.

Model: gpt-6.1-sol. Harness: Codex in T3 Code.

@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:M 30-99 changed lines (additions + deletions). labels Oct 8, 2026
@Brechard

Copy link
Copy Markdown
Contributor

Verified on a physical Pixel 8, Android 17 (API 37), Gboard, using the standard T3 dark theme and an isolated thread with an unsent synthetic draft.

The retained-focus guard fixes the initial resume quarantine, but I found one remaining step before this device's repro is fully fixed: keep KeyboardStickyView subscribed when the JS visibility flag becomes stale.

I applied this PR's patch (fb7e42440fbc3e960a8efea3529983a5685a5714) to upstream main 42da166d13. After switching to Android Settings and returning, lifecycle logging showed the input remained focused across background → active, then useKeyboardState changed from (true, 344.38) to (false, 0) while the real keyboard stayed visible. The composer then dropped behind it.

The library's Android KeyboardAnimationCallback.syncKeyboardPosition restores the animated height and emits keyboardDidShow; useKeyboardState listens to keyboardWillShow and keyboardDidHide, so its selected visibility/height can retain that late hide. The remaining isKeyboardVisible gate disables the sticky transform despite the restored native animation.

This additional change passed the app-switch and dismiss/reopen sequence:

- enabled={Platform.OS === "ios" || (isKeyboardVisible && !keyboardStateSuspect)}
+ enabled={Platform.OS === "ios" || !keyboardStateSuspect}

The blur/resume quarantine still suppresses the original stale-height-after-send case. Native animation handles ordinary keyboard show/hide; it need not be gated by the cached JS visibility flag.

Evidence (the after captures include this one-line correction in addition to the PR guard):

Before: upstream resume behavior After: guard plus visibility correction
Keyboard open with hidden composer Composer visible above the keyboard after resume

19-second Pixel recording: app switch, return, keyboard dismiss, and refocus. Captions are recording annotations; an idle pause was removed. The after source is the clean upstream checkout plus this PR and the visibility correction above. No native code changed.

Validation: this PR's focused hook/layout tests passed (9 tests), and mobile typecheck and targeted lint completed without errors (React warnings remain). A supplemental local regression reproduces the delayed (false, 0) update: it fails with the visibility gate and passes without it. I also verified repeated app switching and dismiss/refocus in a standalone Android build with the correction. No provider turn was sent; iOS and remote/tunnel transport were not exercised.

Model: gpt-6.1-sol. Harness: Codex in T3 Code.

@juliusmarminge

Copy link
Copy Markdown
Member

Note

Grok responding on behalf of Julius.

Thanks for this! #20803 just landed the fix for #17087 (the composer now stays above the keyboard when an owned input keeps focus through an Android app switch), so I'm closing this one as superseded. If you still see the composer disappear on current main, please open a new issue with a fresh recording.

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

Labels

size:M 30-99 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Android composer disappears after switching apps with the keyboard open

3 participants