From fb4d77aac19c8dfec3a5d21ae545846be28bf4cf Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 1 Oct 2026 13:21:57 -0600 Subject: [PATCH 1/4] K2GO-439 fix(ui): inset content for edge-to-edge on targetSdk 35 Android 15+ forces edge-to-edge, so app content and WebViews drew under the status and navigation bars. Add an EdgeToEdge helper and an app-wide ActivityLifecycleCallbacks owner that pads each activity's content by the system-bar insets; LibraryActivity pads its content and bottom nav directly and SplashActivity stays fullscreen, both opting out via EdgeToEdge.SelfManaged. --- .../appdevforall/k2go/IIABApplication.java | 3 + .../org/appdevforall/k2go/SplashActivity.java | 3 +- .../k2go/redesign/LibraryActivity.java | 7 +- .../org/appdevforall/k2go/ui/EdgeToEdge.java | 64 +++++++++++++++++++ .../k2go/ui/EdgeToEdgeCallbacks.java | 39 +++++++++++ 5 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdge.java create mode 100644 controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdgeCallbacks.java diff --git a/controller/app/src/main/java/org/appdevforall/k2go/IIABApplication.java b/controller/app/src/main/java/org/appdevforall/k2go/IIABApplication.java index e067e3dbf..6d5317aa7 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/IIABApplication.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/IIABApplication.java @@ -71,6 +71,9 @@ public void onCreate() { AnalyticsClient.with(this).applyConsent(); AnalyticsClient.with(this).logFirstRunIfNeeded(); registerActivityLifecycleCallbacks(new ForegroundTracker()); + // K2GO-439: one owner pads every activity's content by the system-bar insets (targetSdk 35 + // edge-to-edge); screens that manage their own insets opt out via EdgeToEdge.SelfManaged. + registerActivityLifecycleCallbacks(new org.appdevforall.k2go.ui.EdgeToEdgeCallbacks()); } /** diff --git a/controller/app/src/main/java/org/appdevforall/k2go/SplashActivity.java b/controller/app/src/main/java/org/appdevforall/k2go/SplashActivity.java index 0c3edca63..02d56c5f3 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/SplashActivity.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/SplashActivity.java @@ -19,7 +19,8 @@ import androidx.core.content.ContextCompat; -public class SplashActivity extends AppCompatActivity { +public class SplashActivity extends AppCompatActivity + implements org.appdevforall.k2go.ui.EdgeToEdge.SelfManaged { private static final long EXIT_AT_MS = 3100L; private static final long EXIT_FADE_MS = 400L; diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/LibraryActivity.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/LibraryActivity.java index 0f72fa889..d72ce42aa 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/LibraryActivity.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/LibraryActivity.java @@ -27,7 +27,8 @@ * (Lottie) that flips to OPEN once the server is reachable. * Phase 2 = runtime gate. Content cards, wizard and Step-2 land in later phases. */ -public class LibraryActivity extends AppCompatActivity implements ServerController.Host { +public class LibraryActivity extends AppCompatActivity + implements ServerController.Host, org.appdevforall.k2go.ui.EdgeToEdge.SelfManaged { private static final String TAG = "K2Go-Library"; private static final long GATE_SAFETY_MS = 25000L; @@ -131,6 +132,10 @@ protected void onCreate(Bundle savedInstanceState) { bottomNav = findViewById(R.id.k2go_bottom_nav); railNav = findViewById(R.id.k2go_nav_rail); + // K2GO-439: targetSdk 35 forces edge-to-edge; keep the fragment content below the status bar + // and the bottom nav above the navigation bar instead of drawing under the system bars. + org.appdevforall.k2go.ui.EdgeToEdge.padTop(findViewById(R.id.k2go_nav_host)); + org.appdevforall.k2go.ui.EdgeToEdge.padBottom(bottomNav); NavigationBarView.OnItemSelectedListener navListener = item -> { if (!navSyncing) { currentTab = item.getItemId(); diff --git a/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdge.java b/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdge.java new file mode 100644 index 000000000..b98d2273e --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdge.java @@ -0,0 +1,64 @@ +/* + * ============================================================================ + * Name : EdgeToEdge.java + * Author : AppDevForAll + * Copyright : Copyright (c) 2026 AppDevForAll + * Description : Shared system-bar inset handling (K2GO-439). targetSdk 35 forces + * edge-to-edge on Android 15+, so app content draws behind the + * status bar and the navigation bar. Each screen pads the right + * views by the system-bar (and display-cutout) insets so content + * keeps clear of the bars. Single source for the rule; the call + * site is per-screen because the activities share no base class. + * See https://developer.android.com/develop/ui/views/layout/edge-to-edge + * ============================================================================ + */ +package org.appdevforall.k2go.ui; + +import android.view.View; + +import androidx.core.graphics.Insets; +import androidx.core.view.ViewCompat; +import androidx.core.view.WindowInsetsCompat; + +/** Stateless helper that pads a view by the system-bar insets, preserving its original padding. */ +public final class EdgeToEdge { + + private EdgeToEdge() {} + + /** + * Activities that manage their own system-bar insets, or want none (immersive, splash), + * implement this to opt out of the global {@link EdgeToEdgeCallbacks} handler. + */ + public interface SelfManaged {} + + /** Pad the view's top by the status-bar (and top cutout) inset. */ + public static void padTop(View v) { apply(v, true, false, false, false); } + + /** Pad the view's bottom by the navigation-bar inset. */ + public static void padBottom(View v) { apply(v, false, false, false, true); } + + /** Pad the view on every system-bar edge (top, bottom, and the sides for cutouts/landscape). */ + public static void padAll(View v) { apply(v, true, true, true, true); } + + private static void apply(final View v, final boolean top, final boolean left, + final boolean right, final boolean bottom) { + if (v == null) return; + // Capture the view's own padding once so repeated inset passes (e.g. rotation) never accumulate. + final int pl = v.getPaddingLeft(); + final int pt = v.getPaddingTop(); + final int pr = v.getPaddingRight(); + final int pb = v.getPaddingBottom(); + ViewCompat.setOnApplyWindowInsetsListener(v, (view, insets) -> { + Insets bars = insets.getInsets( + WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout()); + view.setPadding( + left ? pl + bars.left : pl, + top ? pt + bars.top : pt, + right ? pr + bars.right : pr, + bottom ? pb + bars.bottom : pb); + return insets; + }); + // Insets may have already been delivered before the listener was attached; request a fresh pass. + ViewCompat.requestApplyInsets(v); + } +} diff --git a/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdgeCallbacks.java b/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdgeCallbacks.java new file mode 100644 index 000000000..900098763 --- /dev/null +++ b/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdgeCallbacks.java @@ -0,0 +1,39 @@ +/* + * ============================================================================ + * Name : EdgeToEdgeCallbacks.java + * Author : AppDevForAll + * Copyright : Copyright (c) 2026 AppDevForAll + * Description : One owner for edge-to-edge insets (K2GO-439). Registered from + * IIABApplication, it pads every activity's content by the system + * bars so nothing draws under the status / navigation bars on + * targetSdk 35 (Android 15+). Screens that manage their own insets + * (LibraryActivity) or want none (PortalActivity immersive mode, + * SplashActivity) opt out via EdgeToEdge.SelfManaged. + * ============================================================================ + */ +package org.appdevforall.k2go.ui; + +import android.app.Activity; +import android.app.Application; +import android.os.Bundle; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; + +/** Applies system-bar insets to each activity's content view, except opted-out screens. */ +public final class EdgeToEdgeCallbacks implements Application.ActivityLifecycleCallbacks { + + @Override + public void onActivityCreated(@NonNull Activity a, @Nullable Bundle savedInstanceState) { + if (a instanceof EdgeToEdge.SelfManaged) return; + // android.R.id.content is the frame that holds setContentView's view; padding it insets the screen. + EdgeToEdge.padAll(a.findViewById(android.R.id.content)); + } + + @Override public void onActivityStarted(@NonNull Activity a) {} + @Override public void onActivityResumed(@NonNull Activity a) {} + @Override public void onActivityPaused(@NonNull Activity a) {} + @Override public void onActivityStopped(@NonNull Activity a) {} + @Override public void onActivitySaveInstanceState(@NonNull Activity a, @NonNull Bundle outState) {} + @Override public void onActivityDestroyed(@NonNull Activity a) {} +} From 60c58612e2131f2eebcac8a7d5dec53a9bb8cf9d Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 1 Oct 2026 13:35:48 -0600 Subject: [PATCH 2/4] K2GO-439 fix(ui): apply code-review findings on the edge-to-edge slice Opt HelpViewerActivity out of the global handler (its fitsSystemWindows layout already insets) and document the SelfManaged rule; pad the LibraryActivity content container so the rail and both orientations are covered; clear the IME in the global handler so fields are not hidden; extract a unit-tested resolvePadding; fix the stale PortalActivity comments. --- .../k2go/redesign/HelpViewerActivity.java | 3 +- .../k2go/redesign/LibraryActivity.java | 8 +-- .../org/appdevforall/k2go/ui/EdgeToEdge.java | 49 ++++++++++++++----- .../k2go/ui/EdgeToEdgeCallbacks.java | 16 +++--- .../src/main/res/layout/activity_library.xml | 1 + .../appdevforall/k2go/ui/EdgeToEdgeTest.java | 48 ++++++++++++++++++ 6 files changed, 101 insertions(+), 24 deletions(-) create mode 100644 controller/app/src/test/java/org/appdevforall/k2go/ui/EdgeToEdgeTest.java diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/HelpViewerActivity.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/HelpViewerActivity.java index 4a93ef7df..9bee87feb 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/HelpViewerActivity.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/HelpViewerActivity.java @@ -60,7 +60,8 @@ * add one and for the rootfs-served docs. In-manual links stay in the WebView; external links open * in the system browser. */ -public class HelpViewerActivity extends AppCompatActivity { +public class HelpViewerActivity extends AppCompatActivity + implements org.appdevforall.k2go.ui.EdgeToEdge.SelfManaged { private static final String TAG = "K2Go-Help"; private static final String APPASSETS_HOST = "appassets.androidplatform.net"; diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/LibraryActivity.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/LibraryActivity.java index d72ce42aa..bcb7f318f 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/LibraryActivity.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/LibraryActivity.java @@ -132,10 +132,10 @@ protected void onCreate(Bundle savedInstanceState) { bottomNav = findViewById(R.id.k2go_bottom_nav); railNav = findViewById(R.id.k2go_nav_rail); - // K2GO-439: targetSdk 35 forces edge-to-edge; keep the fragment content below the status bar - // and the bottom nav above the navigation bar instead of drawing under the system bars. - org.appdevforall.k2go.ui.EdgeToEdge.padTop(findViewById(R.id.k2go_nav_host)); - org.appdevforall.k2go.ui.EdgeToEdge.padBottom(bottomNav); + // K2GO-439: targetSdk 35 forces edge-to-edge. Pad the content container (rail + content + bottom + // nav) by the system bars so nothing draws under them in either orientation; the boot-gate overlay + // is a sibling and stays full-screen. No IME inset here, so the bottom nav does not ride the keyboard. + org.appdevforall.k2go.ui.EdgeToEdge.padAll(findViewById(R.id.k2go_content_root)); NavigationBarView.OnItemSelectedListener navListener = item -> { if (!navSyncing) { currentTab = item.getItemId(); diff --git a/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdge.java b/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdge.java index b98d2273e..379223031 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdge.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdge.java @@ -26,22 +26,30 @@ public final class EdgeToEdge { private EdgeToEdge() {} /** - * Activities that manage their own system-bar insets, or want none (immersive, splash), - * implement this to opt out of the global {@link EdgeToEdgeCallbacks} handler. + * Activities that manage their own system-bar insets, that want none (a full-screen splash), + * or whose layout already consumes insets (android:fitsSystemWindows, or an inset listener of + * their own) implement this to opt out of the global {@link EdgeToEdgeCallbacks} handler, so the + * insets are never applied twice. */ public interface SelfManaged {} /** Pad the view's top by the status-bar (and top cutout) inset. */ - public static void padTop(View v) { apply(v, true, false, false, false); } + public static void padTop(View v) { apply(v, true, false, false, false, false); } /** Pad the view's bottom by the navigation-bar inset. */ - public static void padBottom(View v) { apply(v, false, false, false, true); } + public static void padBottom(View v) { apply(v, false, false, false, true, false); } /** Pad the view on every system-bar edge (top, bottom, and the sides for cutouts/landscape). */ - public static void padAll(View v) { apply(v, true, true, true, true); } + public static void padAll(View v) { apply(v, true, true, true, true, false); } + + /** + * Like {@link #padAll} but the bottom edge also clears the soft keyboard (IME), so a focused + * field is not hidden. Use for a screen's own content, not for a bottom bar that should stay put. + */ + public static void padAllAndIme(View v) { apply(v, true, true, true, true, true); } private static void apply(final View v, final boolean top, final boolean left, - final boolean right, final boolean bottom) { + final boolean right, final boolean bottom, final boolean ime) { if (v == null) return; // Capture the view's own padding once so repeated inset passes (e.g. rotation) never accumulate. final int pl = v.getPaddingLeft(); @@ -49,16 +57,31 @@ private static void apply(final View v, final boolean top, final boolean left, final int pr = v.getPaddingRight(); final int pb = v.getPaddingBottom(); ViewCompat.setOnApplyWindowInsetsListener(v, (view, insets) -> { - Insets bars = insets.getInsets( - WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout()); - view.setPadding( - left ? pl + bars.left : pl, - top ? pt + bars.top : pt, - right ? pr + bars.right : pr, - bottom ? pb + bars.bottom : pb); + int mask = WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(); + if (ime) mask |= WindowInsetsCompat.Type.ime(); + Insets bars = insets.getInsets(mask); + int[] p = resolvePadding(pl, pt, pr, pb, bars.left, bars.top, bars.right, bars.bottom, + top, left, right, bottom); + view.setPadding(p[0], p[1], p[2], p[3]); return insets; }); // Insets may have already been delivered before the listener was attached; request a fresh pass. ViewCompat.requestApplyInsets(v); } + + /** + * Pure rule: the new [left, top, right, bottom] padding = the original padding plus the inset on + * each selected edge. Adds to the captured original (never to the already-padded value), so an + * inset pass is idempotent. Package-private for unit testing. + */ + static int[] resolvePadding(int pl, int pt, int pr, int pb, + int il, int it, int ir, int ib, + boolean top, boolean left, boolean right, boolean bottom) { + return new int[]{ + left ? pl + il : pl, + top ? pt + it : pt, + right ? pr + ir : pr, + bottom ? pb + ib : pb, + }; + } } diff --git a/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdgeCallbacks.java b/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdgeCallbacks.java index 900098763..1a7188ec3 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdgeCallbacks.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/ui/EdgeToEdgeCallbacks.java @@ -5,10 +5,13 @@ * Copyright : Copyright (c) 2026 AppDevForAll * Description : One owner for edge-to-edge insets (K2GO-439). Registered from * IIABApplication, it pads every activity's content by the system - * bars so nothing draws under the status / navigation bars on - * targetSdk 35 (Android 15+). Screens that manage their own insets - * (LibraryActivity) or want none (PortalActivity immersive mode, - * SplashActivity) opt out via EdgeToEdge.SelfManaged. + * bars (and the IME) so nothing draws under the status / navigation + * bars or the keyboard on targetSdk 35 (Android 15+). Screens that + * manage their own insets (LibraryActivity), whose layout already + * consumes them (HelpViewerActivity via fitsSystemWindows), or want + * none (SplashActivity) opt out via EdgeToEdge.SelfManaged. + * PortalActivity stays in: its immersive mode zeroes the insets when + * it hides the bars, so the one handler covers both of its states. * ============================================================================ */ package org.appdevforall.k2go.ui; @@ -20,14 +23,15 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; -/** Applies system-bar insets to each activity's content view, except opted-out screens. */ +/** Applies system-bar and IME insets to each activity's content view, except opted-out screens. */ public final class EdgeToEdgeCallbacks implements Application.ActivityLifecycleCallbacks { @Override public void onActivityCreated(@NonNull Activity a, @Nullable Bundle savedInstanceState) { if (a instanceof EdgeToEdge.SelfManaged) return; // android.R.id.content is the frame that holds setContentView's view; padding it insets the screen. - EdgeToEdge.padAll(a.findViewById(android.R.id.content)); + // These screens have no bottom bar, so clearing the keyboard (IME) on the bottom is safe. + EdgeToEdge.padAllAndIme(a.findViewById(android.R.id.content)); } @Override public void onActivityStarted(@NonNull Activity a) {} diff --git a/controller/app/src/main/res/layout/activity_library.xml b/controller/app/src/main/res/layout/activity_library.xml index d068d6525..a14a76e77 100644 --- a/controller/app/src/main/res/layout/activity_library.xml +++ b/controller/app/src/main/res/layout/activity_library.xml @@ -9,6 +9,7 @@ medium/expanded (>= 600dp wide); the reverse in compact. Toggled at runtime by width in LibraryActivity, so rotation never recreates the activity (the boot gate is preserved). --> diff --git a/controller/app/src/test/java/org/appdevforall/k2go/ui/EdgeToEdgeTest.java b/controller/app/src/test/java/org/appdevforall/k2go/ui/EdgeToEdgeTest.java new file mode 100644 index 000000000..7ee0d599b --- /dev/null +++ b/controller/app/src/test/java/org/appdevforall/k2go/ui/EdgeToEdgeTest.java @@ -0,0 +1,48 @@ +package org.appdevforall.k2go.ui; + +import static org.junit.Assert.assertArrayEquals; + +import org.junit.Test; + +/** + * Unit tests for {@link EdgeToEdge#resolvePadding} (K2GO-439): the pure rule that adds the selected + * inset edges to the view's original padding. Padding is [left, top, right, bottom]. + */ +public class EdgeToEdgeTest { + + @Test + public void padAllAddsEveryEdgeToTheOriginal() { + assertArrayEquals(new int[]{15, 25, 35, 45}, + EdgeToEdge.resolvePadding(5, 5, 5, 5, 10, 20, 30, 40, + true, true, true, true)); + } + + @Test + public void padTopAddsOnlyTheTopEdge() { + assertArrayEquals(new int[]{5, 25, 5, 5}, + EdgeToEdge.resolvePadding(5, 5, 5, 5, 10, 20, 30, 40, + true, false, false, false)); + } + + @Test + public void padBottomAddsOnlyTheBottomEdge() { + assertArrayEquals(new int[]{5, 5, 5, 45}, + EdgeToEdge.resolvePadding(5, 5, 5, 5, 10, 20, 30, 40, + false, false, false, true)); + } + + @Test + public void noEdgesKeepsTheOriginalPadding() { + assertArrayEquals(new int[]{5, 5, 5, 5}, + EdgeToEdge.resolvePadding(5, 5, 5, 5, 10, 20, 30, 40, + false, false, false, false)); + } + + @Test + public void addsToTheOriginalNotToZero() { + // The original padding is preserved and the inset is added on top (not replaced). + assertArrayEquals(new int[]{100, 107, 100, 100}, + EdgeToEdge.resolvePadding(100, 100, 100, 100, 1, 7, 3, 9, + true, false, false, false)); + } +} From b3f68c64a20972dba1a89987f6c3ad2ff27a1e45 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 1 Oct 2026 23:14:31 -0600 Subject: [PATCH 3/4] K2GO-439 fix(ui): drop terminal adjustResize and inset the install overlay Final-review fixes: remove windowSoftInputMode=adjustResize from TerminalActivity so the global padAllAndIme is the single IME handler, and pad the install_progress overlay (a sibling of the content container) so its controls clear the navigation bar. --- controller/app/src/main/AndroidManifest.xml | 5 +++-- .../java/org/appdevforall/k2go/redesign/LibraryActivity.java | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/controller/app/src/main/AndroidManifest.xml b/controller/app/src/main/AndroidManifest.xml index 6c3bd9c3e..cba273c96 100644 --- a/controller/app/src/main/AndroidManifest.xml +++ b/controller/app/src/main/AndroidManifest.xml @@ -73,12 +73,13 @@ the IME. singleTop lets the keep-alive notification's SINGLE_TOP intent reuse the running instance. Not exported: launched only in-app (redesign Settings, the terminal's own notification PendingIntent). --> + + android:configChanges="orientation|screenSize|screenLayout|keyboardHidden" /> { if (!navSyncing) { currentTab = item.getItemId(); From 3d01108230fc002713d8f4087307f9ce1d6c9280 Mon Sep 17 00:00:00 2001 From: Luis Guzman Date: Thu, 1 Oct 2026 23:46:53 -0600 Subject: [PATCH 4/4] K2GO-439 fix(ui): remove the double bottom inset under the bottom nav The Material BottomNavigationView and NavigationRailView inset themselves for the navigation bar, so padding the content container's bottom too left a large empty gap under the nav (seen on Settings). Pad only the top of the content for the status bar and let the Material nav components handle the bottom; drop the now-unused content container id. --- .../org/appdevforall/k2go/redesign/LibraryActivity.java | 9 +++++---- controller/app/src/main/res/layout/activity_library.xml | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/controller/app/src/main/java/org/appdevforall/k2go/redesign/LibraryActivity.java b/controller/app/src/main/java/org/appdevforall/k2go/redesign/LibraryActivity.java index a08766671..abbd186d3 100644 --- a/controller/app/src/main/java/org/appdevforall/k2go/redesign/LibraryActivity.java +++ b/controller/app/src/main/java/org/appdevforall/k2go/redesign/LibraryActivity.java @@ -132,10 +132,11 @@ protected void onCreate(Bundle savedInstanceState) { bottomNav = findViewById(R.id.k2go_bottom_nav); railNav = findViewById(R.id.k2go_nav_rail); - // K2GO-439: targetSdk 35 forces edge-to-edge. Pad the content container (rail + content + bottom - // nav) by the system bars so nothing draws under them in either orientation; the boot-gate overlay - // is a sibling and stays full-screen. No IME inset here, so the bottom nav does not ride the keyboard. - org.appdevforall.k2go.ui.EdgeToEdge.padAll(findViewById(R.id.k2go_content_root)); + // K2GO-439: targetSdk 35 forces edge-to-edge. Pad only the TOP of the fragment content for the + // status bar; the Material BottomNavigationView and NavigationRailView inset THEMSELVES for the + // navigation bar by default, so padding the container's bottom too would double the inset (a gap + // under the nav). The boot-gate overlay is a sibling and stays full-screen. + org.appdevforall.k2go.ui.EdgeToEdge.padTop(findViewById(R.id.k2go_nav_host)); // K2GO-439: the install overlay is a sibling of the content container (not inside it), so inset // its bottom too, else its controls draw under the navigation bar during install. org.appdevforall.k2go.ui.EdgeToEdge.padBottom(findViewById(R.id.k2go_install_progress)); diff --git a/controller/app/src/main/res/layout/activity_library.xml b/controller/app/src/main/res/layout/activity_library.xml index a14a76e77..d068d6525 100644 --- a/controller/app/src/main/res/layout/activity_library.xml +++ b/controller/app/src/main/res/layout/activity_library.xml @@ -9,7 +9,6 @@ medium/expanded (>= 600dp wide); the reverse in compact. Toggled at runtime by width in LibraryActivity, so rotation never recreates the activity (the boot gate is preserved). -->