Skip to content

Fix NavBarDialog layout when soft keyboard opens#983

Open
BoringMan314 wants to merge 1 commit into
Nain57:dev-4.0.0-beta05-fixesfrom
BoringMan314:fix/navbar-dialog-ime-lift
Open

Fix NavBarDialog layout when soft keyboard opens#983
BoringMan314 wants to merge 1 commit into
Nain57:dev-4.0.0-beta05-fixesfrom
BoringMan314:fix/navbar-dialog-ime-lift

Conversation

@BoringMan314

Copy link
Copy Markdown
Contributor

Summary

  • Fix floating NavBarDialog layouts (e.g. Scenario settings) where opening the soft keyboard could hide the bottom navigation bar and leave a large empty gap in the content area.
  • Plain OverlayDialogs (e.g. trigger/event pages) can use SOFT_INPUT_ADJUST_RESIZE. NavBarDialog cannot: in portrait the bottom bar is attached to the CoordinatorLayout, and ADJUST_RESIZE parks the sheet oddly on some devices.
  • Change: NavBarDialog uses SOFT_INPUT_ADJUST_NOTHING and lifts the sheet plus bottom bar with the IME (via WindowInsetsAnimationCompat, with a short fallback animation when overlay insets are incomplete).

Changes

Notes

@Nain57
Nain57 changed the base branch from master to dev-4.0.0-beta05-fixes July 15, 2026 08:29

@Nain57 Nain57 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi and thank you for your contributions.

I see you include a commit included in another PR. Please either commit all your fixes on the same branch, or make them atomic.


private fun setupPortraitViews() {
dialogCoordinatorLayout?.apply {
// Add the navigation bar.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a lot of additions dedicated to ime management, this should be extracted in a separate class

insets
}
decor.viewTreeObserver.addOnGlobalLayoutListener(imeLayoutListener)
ViewCompat.requestApplyInsets(decor)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code installs three separate listeners that all try to do the same thing:

// (a) WindowInsetsAnimationCompat.Callback  → onProgress / onEnd
// (b) setOnApplyWindowInsetsListener        → inset dispatch
// (c) OnGlobalLayoutListener                → layout fallback

The imeLayoutListener (c) is described as a fallback for accessibility overlays that skip inset animations, but it fires on every layout pass; including those caused by the translation animations in (a). This creates a feedback loop risk: applyImeLift triggers a layout change → imeLayoutListener fires → updateImeLiftAnimated is called → if imeAnimationRunning is false it starts another animator, etc.

The imeAnimationRunning flag partially guards this, but onEnd clears it and then calls updateImeLiftAnimated() synchronously, which starts a new animator. If that animator's addUpdateListener triggers another layout, the flag won't be set (no animation running at that moment), so imeLayoutListener will call updateImeLiftAnimated again; potentially firing a second animator on top of an existing one.

Remove the OnGlobalLayoutListener fallback or scope it strictly (set a flag during the animation window and remove the listener once a first valid inset is received). The three-mechanism approach is the main source of complexity and potential bugs.

(method.invoke(imm) as? Int) ?: 0
} catch (_: Throwable) {
0
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

getInputMethodWindowVisibleHeight is a hidden @hide API that has been removed/renamed across Android versions. It will throw NoSuchMethodException on some devices (silently caught and returning 0), making the fallback unreliable; and the try/catch pattern swallows all exceptions including unexpected ones.

Replace the reflection with ViewCompat.getRootWindowInsets(decor)?.isVisible(WindowInsetsCompat.Type.ime()) as the truth source — you're already using it in updateImeLiftAnimated.

private fun applyImeLift(coordinator: CoordinatorLayout, imeBottomPx: Int) {
if (imeBottomPx == lastImeLiftPx) return
lastImeLiftPx = imeBottomPx
for (i in 0 until coordinator.childCount) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Translating each child individually means any view added or removed while the IME is up will start at translationY = 0, causing a visual jump. It would be safer to translate the coordinator itself (or its parent), or to set translationY in a post-layout pass.

?.bottom
?: 0
val lift = max(insetIme, inputMethodVisibleHeight())
val threshold = (decor.resources.displayMetrics.heightPixels * 0.12f).toInt()

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

12% of screen height is used to distinguish "real IME showing" from noise, but this is undocumented and device-dependent. A 12% threshold on a small phone (~720px) is ~86px, which may suppress legitimate small IME appearances (e.g. number pad on some OEMs). This deserves at minimum a named constant and a comment.

Extract IME lift into NavBarDialogImeLiftController. Drive lift from
WindowInsets animations + apply-insets only (no GlobalLayout fallback /
hidden IMM API). Translate the coordinator itself and use IME visibility
from WindowInsetsCompat.
@BoringMan314

Copy link
Copy Markdown
Contributor Author

Updated to address the review:

  • Branch is now atomic: single commit on top of dev-4.0.0-beta05-fixes (#982 BadToken change is already on the base — no longer duplicated here).
  • IME lift logic extracted to NavBarDialogImeLiftController.
  • Removed OnGlobalLayoutListener fallback and getInputMethodWindowVisibleHeight reflection.
  • Translate the coordinator itself instead of each child.
  • Use WindowInsetsCompat.isVisible(Type.ime()) instead of the previous screen-height threshold.

@BoringMan314
BoringMan314 force-pushed the fix/navbar-dialog-ime-lift branch from 1685c5c to b611759 Compare July 15, 2026 10:53
@Nain57

Nain57 commented Jul 15, 2026

Copy link
Copy Markdown
Owner

Needs carefull review and testing, i'll come back to you once everything is OK from my side.

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.

2 participants