From 65148df9d7bc54b2bb29b59fe93d1b6cd4296cb8 Mon Sep 17 00:00:00 2001 From: will wade Date: Sun, 25 Oct 2026 09:33:17 +0000 Subject: [PATCH] =?UTF-8?q?fix(ime):=20stuck=20'Preparing=20Dasher'=20over?= =?UTF-8?q?lay=20after=20view=20recreation=20=E2=80=94=20the=20real=20'flo?= =?UTF-8?q?at-like=20dead=20window'=20(#76)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Heide on v0.1.24 (MuMu emulator): "It starts with a big float LIKE window, but you can't type on it. You have to tap on Float first and then Dock." Root cause — a regression from #74's engine re-entry guard. When onCreateInputView runs again on a LIVE instance (configuration change: rotation, fold, MuMu's freely-resizable window; OEM IME-switch flows that keep the service alive), the fresh view tree builds a NEW loading overlay (visible by default), but createEngine() returns at the guard BEFORE the line that hides it. The opaque overlay then covers the canvas forever: a big, dead, overlay-looking panel — indistinguishable from 'float mode' to a user, and untypeable. The Float/Dock buttons sit OUTSIDE the overlay, which is why tapping Float then Dock appeared to revive it (each transition forces relayouts and, on some devices, a process/service restart). This also re-explains the whole #73 timeline: 'still starts in float mode / fine unless you switch keyboards / doesn't stay docked' were all this dead-canvas state, not the mode preference. v0.1.23 shipped the #74 guard — exactly when her 'doesn't stay docked' report arrived. Reproduced on the API 33 emulator (MuMu's generation): rotate with the keyboard up → same-instance recreate → 0.3s guard-path restore (vs 3-4s full create) → zero colorful canvas pixels. Fix: the guard path now hides the fresh overlay and re-pushes the canvas geometry to the surviving engine. Post-fix on the same repro: canvas renders (500 colorful px) and responds to zoom (106 px change during a drag). Also adds an output-event Log.d breadcrumb (non-empty events only) for future support diagnostics. Signed-off-by: will wade --- .../at/dasher/android/DasherImeService.kt | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/at/dasher/android/DasherImeService.kt b/app/src/main/java/at/dasher/android/DasherImeService.kt index edb722f..0990c7f 100644 --- a/app/src/main/java/at/dasher/android/DasherImeService.kt +++ b/app/src/main/java/at/dasher/android/DasherImeService.kt @@ -509,13 +509,25 @@ class DasherImeService : InputMethodService() { private fun createEngine() { // Re-entry guard (#73): onCreateInputView can run again on a LIVE - // instance (configuration change; some OEM IME-switch flows keep the - // service alive). The engine is per-process by design ("created once - // per process", #53/#56) — a second one would leak the first with - // BOTH running dasher_frame loops into the same canvas. Reuse it; - // the new canvas wires itself through the fields, and its size - // callback reaches the engine (it is no longer null here). - if (engine != null) return + // instance (configuration change — rotation, fold, MuMu's resizable + // window; some OEM IME-switch flows keep the service alive). The + // engine is per-process by design ("created once per process", + // #53/#56) — a second one would leak the first with BOTH running + // dasher_frame loops into the same canvas. Reuse it. + if (engine != null) { + // #76: the recreated view tree built a FRESH loading overlay + // (visible by default) — the hide at the end of this function + // sits below the guard and never ran, so the keyboard came up + // as a dead, overlay-covered panel ("a big float-like window + // you can't type on"; repro'd via rotation on the API 33 + // emulator: zero colorful canvas pixels). Hide it now and + // re-push the canvas geometry to the surviving engine. + loadingOverlay?.visibility = View.GONE + canvasView?.let { v -> + if (v.width > 0 && v.height > 0) engine?.onSurfaceSizeChanged(v.width, v.height) + } + return + } val dataDir = DataInstaller.ensureInstalled(this) // User state (settings, training) lives outside the data dir so a // data re-extraction can never wipe it. @@ -652,6 +664,9 @@ class DasherImeService : InputMethodService() { if (type != 0 && type != 1 && type != 2) { Log.w(TAG, "Unknown engine output event type $type (text='${text.take(32)}')") } + if (text.isNotEmpty()) { + Log.d(TAG, "output event type=$type len=${text.length}") + } outputCoalescer.onEngineOutput(type, text) } eng.setMessageListener { _, msg ->