Skip to content

feat(ime): remember dock/float choice across keyboard shows - #64

Merged
willwade merged 1 commit into
mainfrom
feat/ime-mode-persistence
Sep 18, 2026
Merged

willwade merged 1 commit into
mainfrom
feat/ime-mode-persistence

Conversation

@willwade

Copy link
Copy Markdown
Contributor

What

Persists the last-used dock/float mode (SharedPreferences, dasher_ime/start_mode) and restores it after engine creation on the next keyboard show. Docked stays the default.

Why

Heide's v0.1.20 feedback: she wants the keyboard to "start directly as docked, like other keyboards do" — on v0.1.16/Android 16 she had to tap Float then Dock to get a working keyboard (that initial-show bug is already fixed by #60's targetSdk 36; this PR makes the preference stick regardless).

Guards

  • Floating restore only runs when canDrawOverlays is already granted — never bounces the user to system settings on open
  • A failed float (addView exception fallback path) does not persist floating
  • Button wiring moved to a floatDockBtn field (was a local passed as a parameter); nulled in onDestroy

Test plan

  • Fresh install: keyboard opens docked
  • Float → hide → reopen: opens floating
  • Dock → hide → reopen: opens docked
  • Without overlay permission + saved floating: opens docked, no settings bounce
  • ./gradlew :app:compileDebugKotlin :app:lintDebug clean (verified locally)

Refs #61 #63 (same feedback email).

Heide (v0.1.20 feedback): "is there a way to have to start it directly
as docked, like other keyboards do? Because as of right now, when the
keyboard pops up, you have to tap on float, and then dock."

The IME already starts docked — her float→dock dance is the v0.1.16
Android-16 initial-show workaround (fixed by targetSdk 36 in #60).
Regardless, the last-used mode should persist: save 'floating'/'docked'
in SharedPreferences on every mode change and restore it after engine
creation on the next show. Docked remains the default, and the floating
restore only runs when the overlay permission is already granted so a
permission-less user is never bounced to system settings on open.

Refs #61 #63 (same feedback email).

Signed-off-by: will wade <willwade@gmail.com>
@willwade
willwade merged commit 5b2c001 into main Sep 18, 2026
2 checks passed
@willwade
willwade deleted the feat/ime-mode-persistence branch September 18, 2026 16:10
willwade added a commit that referenced this pull request Oct 15, 2026
…e restore, engine re-entry guard, unbind cleanup (#74)

Fixes #73.

Heide (testing v0.1.21, before v0.1.22): "The IME still starts in float
mode, then you have to tap on dock. Then it's fine unless you switch
keyboards."

**Not a v0.1.22 regression** — v0.1.22's IME changes are output batching
+ force-show. This hardens #64's mode-remember machinery, which has
three lifecycle holes found by inspection:

1. **`onCreateInputView` re-entry on a live instance** (config change;
OEM IME-switch flows that keep the service alive): `createEngine()`
built a *second* engine — the first kept its `dasher_frame` loop, both
submitting into the same canvas — and a stale `floating=true` made the
posted restore early-return inside `enterFloatingMode()`, presenting a
dead overlay canvas as "floating" over a full-height docked root. Tap
Dock → clean; switch keyboards → repeats. This matches the report
verbatim.
2. **No `onUnbindInput` cleanup** — a surviving service left the overlay
hovering over the next keyboard.
3. **`exitFloatingMode` early-returned on `floatingView == null`** — a
swallowed `removeView` failure (Samsung overlay quirks, cf. #46)
stranded a dead overlay over every subsequent show.

## Changes (single file, +51/−4)

- `createEngine()`: re-entry guard — the engine is per-process by design
(#53/#56)
- Posted restore: **self-heal** — `if (floating)
exitFloatingMode(persist = false)` *before* evaluating the saved
preference, so every keyboard show converges to the persisted mode
regardless of view recreation
- `onUnbindInput()`: exit floating without persisting (saved mode stays
the user's deliberate choice)
- `exitFloatingMode(persist)`: never early-returns on a missing overlay
window; `removeView` failures now log

## Testing

- [x] `:app:assembleDebug` + `:app:testDebugUnitTest` green locally
- [ ] Manual on Heide's device (Samsung/Android 16): dock → switch to
SwiftKey → switch back → still docked, no dead overlay
- [ ] Float → switch away → back: overlay gone during the other
keyboard, float restored on return

Given #71's human review is pending, happy to fold this into the same
review session.

Signed-off-by: will wade <willwade@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant