Repository navigation
feat(ime): 'Always start docked' setting + float-mode diagnostics (#73 round 3) - #75
Merged
Merged
Conversation
…d 3) Heide on v0.1.23: "It's still starts in float mode. And this time, it doesn't stay in docked mode when you bring the keyboard up again. But one thing that's fixed though, is the letters are not right on top of your cursor… better than the last version." The v0.1.23 fix (#74) did not fix her device, and a full emulator repro attempt (API 35, edge-to-edge enforced) could NOT reproduce the float restart — the docked keyboard lays out correctly there, including IME switch cycles. Remaining hypotheses are Samsung/OneUI-specific and need data from the affected device. This change does three things: 1. Escape hatch (product-level, Heide has never wanted float — three reports): new Settings → Input → 'Keyboard always starts docked' toggle (dasher_ime/always_docked). When set, the saved float preference is ignored on every keyboard show. Verified on the emulator across the full matrix: unset->docked; floating+alwaysDocked ->docked; floating without the pin ->floating restore still works. 2. Diagnostics: every mode restore now captures ime_mode_restore with the decision inputs (saved_mode, always_docked, floating_before, restored_floating, overlay_perm — opt-in analytics, no-op otherwise), and transitions capture ime_float_enter/exit with reasons (button/restore/selfheal/unbind). If float still appears for Heide after this, the next session tells us exactly which path. 3. Hardening kept from the repro session: setDockedHeight now sets Gravity.BOTTOM explicitly (a no-gravity FrameLayout child defaults TOP|START in the IME decor — never correct, even if current platform layout paths happen to re-dock it). Log.d lifecycle breadcrumbs for onCreateInputView/onStartInputView/onUnbindInput/ onDestroy/createEngine-restore. Note: #74's fixes (engine re-entry guard, self-healing restore, unbind cleanup) remain — they closed real holes by inspection, they just weren't HER hole. Signed-off-by: will wade <willwade@gmail.com>
… ImePrefs constants, gate truth table Addresses the #75 review (7/10): - I-1: floating_before was captured AFTER the self-heal exit — always false, neutering the single most valuable diagnostic (#73's exact path). Captured before the exit now. - I-2: ime_mode_restore / ime_float_enter / ime_float_exit were outside the published analytics-events.json — 'if it's not in this schema, it's not collected'. All three added (properties, reason enums, pii:false), plus ime_always_docked_changed (M7) and android in the platform/app_variant examples. - I-3: dasher_ime/always_docked/start_mode/emoji_return_alphabet literals were spread across three files — a typo in any one silently broke the gate. ImePrefs object, used everywhere. - I-4: the restore decision extracted to pure shouldRestoreFloating(); truth table pinned in ImePrefsTest (pin overrides floating pref; no-permission never restores; unset never restores). - M5/M8: setting now uses stringResource (EN default; locales fall back) and the row is one toggleable touch target with Role.Switch — TalkBack announces label+state together, bigger target for motor-impaired users. - M9: setting description documents the in-session float edge ('Float still floats until you dock — this decides where the keyboard STARTS'). - M6 accepted as-is: Log.d breadcrumbs ship deliberately (support case — logcat from a user device is the fallback when analytics can't reproduce). Signed-off-by: will wade <willwade@gmail.com>
…ort, test inputs, hoisted reads Final polish from the #75 second review (9/10): - M3: onDestroy while floating now emits ime_float_exit (persist=false, reason=destroy — added to the schema enum) so enter/exit correlation stays balanced when the service dies floating. - M1: unused android.content.Context import removed from ImePrefs.kt (contradicted its own 'pure, no Context' doc). - M2: duplicate truth-table row now actually sets the pin (name true to inputs; pins must never force floating either). - Nits: saved-mode read once with a null default (elvis reachable, definite String type); canDrawOverlays hoisted to one read per show. Signed-off-by: will wade <willwade@gmail.com>
willwade
added a commit
that referenced
this pull request
Oct 28, 2026
… + Lucide toolbars + no fullscreen extract (#78) Fixes #76 (round 2 — the real mechanism), plus the icon/toolbar overhaul flagged in hands-on testing. ## The headline fix: the "docked" keyboard was full-screen and top-anchored on API 35 Found in a pixel-verified E2E sweep on the API 35 emulator: `onCreateInputView` sets the docked root's `FrameLayout.LayoutParams(1007px, Gravity.BOTTOM)` **before attachment** (parent=null) — then the IME framework **stamps its own `MATCH_PARENT×WRAP_CONTENT` params over them** when attaching the view to the decor. Measured: root laid out `1080x2272 at [0,0]` — toolbar over the status bar, canvas swallowing the app. That is exactly "I get fullscreen Dasher but I can't see the search field" (hands-on), and is a strong candidate for Heide's entire #73/#76 saga on MuMu: a giant, overlay-looking, untypeable-feeling keyboard. It also explains why #75's `Gravity.BOTTOM` "verified" on the API 33 emulator — that layout path re-docks on its own, so the stamping bug was invisible there. **Fix:** `setDockedHeight` runs again in the posted handler — post-attachment, post-stamp — where the parent is real and the params stick. Verified: toolbar strip at y1400–1540 (bottom), app visible above it, in **portrait and landscape**, surviving **rotation and IME switches**. ## Fullscreen extract disabled `onEvaluateFullscreenMode() = false` — in landscape the default IMS behavior replaces the whole screen with the IME. The keyboard now docks at the bottom in every orientation, consistent with the product stance and Windows/macOS Dasher. ## RFC 0002: Lucide icons + compact toolbars The IME's View-based toolbars used text labels ("⌫", "Sel", "Cp", "Ps") and default Material pills for Float/Hide ("no icons, looks poor, the dock buttons are massive"): - Lucide icons baked as tintable vector drawables (lucide-static v1.62, ISC): arrow-left/right, delete, copy, clipboard-paste, text-select, panel-bottom, picture-in-picture-2, keyboard-off - Editing toolbar: 48dp icon targets (56dp delete), 8dp rounded corners (design token rounded.md), contentDescriptions preserved - Top bar: compact 48dp icon buttons; the Float⇄Dock toggle swaps icon+description+action via `setFloatDockButton`; emoji toggle keeps its "😀"/"Abc" state label in a compact pill - **Pressed states** (design tokens: highlight-light/dark fills) — the flat borders gave zero touch feedback - **Long-press tooltips** (API 26+) on every icon — note: tooltip rendering inside IME windows is unverified; press feedback + TalkBack confirmed working ## Also - `onConfigurationChanged` re-pushes `setSystemAppearance` (RFC 0007: mid-session dark/light flips now re-derive the palette for System-palette users instead of waiting for engine recreation) - Diagnostics: `setDockedHeight`/after-layout/flush Log.d breadcrumbs ## E2E verification (API 35 emulator, pixel + logcat verified) | Scenario | Result | |---|---| | Docked portrait / landscape | ✓ app visible above a bottom-docked keyboard | | Text commits → target field | ✓ (real typing; engine→coalescer→batched flush→InputConnection) | | Float→Dock cycle incl. persistence | ✓ overlay window lifecycle + reasons logged | | IME switch cycle | ✓ clean recreate, canvas alive | | Rotation mid-typing (same-instance recreate) | ✓ canvas alive | | Cold start | ✓ 2.7s to engine ready | | Dark mode | ✓ toolbar switches; canvas follows the palette setting per RFC 0007 | Known-open: tooltip popups may not render inside the IME window (needs eyes; press states and TalkBack cover the affordance), mechanical synthetic typing can't zoom deep enough to re-verify ⌫-on-text (code path unchanged since #71's review). --------- Signed-off-by: will wade <willwade@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Round 3 for Heide's float/dock report, after her v0.1.23 test: "It still starts in float mode. And this time, it doesn't stay in docked mode when you bring the keyboard up again. But one thing that's fixed though, is the letters are not right on top of your cursor… better than the last version."
The v0.1.23 fix (#74) was insufficient. A full reproduction attempt on an API 35 emulator (edge-to-edge enforced, IME switch cycles, hide/show, pref manipulation via run-as) could not reproduce the float restart — the docked keyboard lays out correctly,
mode restorebreadcrumbs show the expected state machine, and the full preference matrix behaves as designed (unset→docked; floating→float restore; floating+always-docked→docked). The remaining trigger is Samsung/OneUI-specific (Heide's Android 16 tablet) and needs data from her device.This PR therefore ships three things:
dasher_ime/always_docked). Heide has never wanted float (three consecutive reports); pinning docked makes the underlying bug irrelevant for her. Float users who want restore keep it when the toggle is off.ime_mode_restoreanalytics event with every decision input (opt-in, no-op otherwise),ime_float_enter/ime_float_exitwith reasons (button/restore/selfheal/unbind), plus Log.d lifecycle breadcrumbs. If float still appears for her after this build, the next session shows exactly which path took it there.setDockedHeightnow setsGravity.BOTTOMexplicitly (a no-gravity FrameLayout child defaults to TOP|START in the IME decor — never the intent, even where platform layout happens to re-dock it).#74's fixes (engine re-entry guard, self-healing restore, unbind cleanup) remain — they closed real holes by inspection; they just weren't her hole.
Testing
:app:assembleDebug+:app:testDebugUnitTestgreenrun-asstate injection + logcat breadcrumbs (docked default / pinned docked / float restore)ime_mode_restoreevent from her session identifies the pathFixes #73 (as far as it can be fixed without her device; diagnostics close the loop either way).