fix(android): keep the app out from under the system bars
Reported from the first device run: the playback controls are off screen. `targetSdk 35` is Android 15, which lays every app out edge-to-edge and ignores the deprecated `statusBarColor` and `navigationBarColor` the scaffold's theme still sets -- so a `match_parent` WebView draws the page's bottom band, which on a phone is the transport *and* the tab bar, underneath the gesture bar. `applyWindowInsets()` pads the container by `systemBars | displayCutout | ime` and returns the insets rather than consuming them, so the WebView is laid out inside them. The keyboard is in the mask because a search box the keyboard covers is the same bug one surface over. The window background goes black to match the app's own default ramp: that padding is what shows through, and a band of the scaffold's blue-grey above and below reads as the app failing to fill the screen. No tier we have can see this class of fault -- a browser viewport has no system bars, so `phone-shell.spec.ts` at 390x844 renders a shell that fits at the moment the device is clipping it. Verified only as far as the APK building; the insets need the next build on a phone.
This commit is contained in:
@@ -31,9 +31,16 @@ import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.core.content.FileProvider;
|
||||
import androidx.core.graphics.Insets;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
import androidx.core.view.WindowCompat;
|
||||
import androidx.core.view.WindowInsetsControllerCompat;
|
||||
import androidx.webkit.WebViewAssetLoader;
|
||||
|
||||
import org.json.JSONObject;
|
||||
@@ -88,6 +95,10 @@ public class MainActivity extends AppCompatActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
// Before anything renders: the page is laid out inside the
|
||||
// window, and on Android 15 the window is the whole screen.
|
||||
applyWindowInsets();
|
||||
|
||||
// Initialize the native Go library
|
||||
bridge = new WailsBridge(this);
|
||||
bridge.initialize();
|
||||
@@ -892,8 +903,61 @@ public class MainActivity extends AppCompatActivity {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keep the web content inside the safe area.
|
||||
*
|
||||
* <p>targetSdk 35 is Android 15, which lays every app out
|
||||
* edge-to-edge and ignores the {@code statusBarColor} and
|
||||
* {@code navigationBarColor} this app's theme still sets. The
|
||||
* WebView is {@code match_parent}, so the page's bottom band -- the
|
||||
* transport and, on a phone, the tab bar -- was drawn underneath the
|
||||
* gesture bar and reported from a device as "I can't see the
|
||||
* playback controls, they seem to be off screen".
|
||||
*
|
||||
* <p>No web-tier test can see this: a browser viewport has no system
|
||||
* bars, so the phone specs at 390x844 render a shell that fits
|
||||
* while the device does not.
|
||||
*
|
||||
* <p>The insets are applied as padding and the window insets are
|
||||
* returned rather than consumed, so the WebView is laid out inside
|
||||
* them. {@code ime()} is in the mask because the same reasoning
|
||||
* covers the keyboard: a focused search box that the keyboard
|
||||
* covers is the same bug one surface over.
|
||||
*/
|
||||
private void applyWindowInsets() {
|
||||
final View container = findViewById(R.id.main_container);
|
||||
|
||||
if (container == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewCompat.setOnApplyWindowInsetsListener(container, (view, windowInsets) -> {
|
||||
Insets insets = windowInsets.getInsets(
|
||||
WindowInsetsCompat.Type.systemBars()
|
||||
| WindowInsetsCompat.Type.displayCutout()
|
||||
| WindowInsetsCompat.Type.ime());
|
||||
|
||||
view.setPadding(insets.left, insets.top, insets.right, insets.bottom);
|
||||
|
||||
return windowInsets;
|
||||
});
|
||||
|
||||
// The padded band shows the window background, which is dark
|
||||
// (this app's own default ramp is black), so the system's icons
|
||||
// have to be the light set or they vanish into it. The theme is
|
||||
// DayNight and would otherwise ask for dark icons in light mode.
|
||||
WindowInsetsControllerCompat controller =
|
||||
WindowCompat.getInsetsController(getWindow(), getWindow().getDecorView());
|
||||
|
||||
controller.setAppearanceLightStatusBars(false);
|
||||
controller.setAppearanceLightNavigationBars(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// The frontend records every navigation as a history entry, so
|
||||
// this is the app's own back stack: `canGoBack()` is false only
|
||||
// at the launch entry, which is where back should leave.
|
||||
if (webView != null && webView.canGoBack()) {
|
||||
webView.goBack();
|
||||
} else {
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
<resources>
|
||||
<color name="wails_blue">#3574D4</color>
|
||||
<color name="wails_blue_dark">#2C5FB8</color>
|
||||
<color name="wails_background">#1B2636</color>
|
||||
<!-- The window background, which is what the launch screen shows and
|
||||
what the system-bar padding leaves visible. Black rather than the
|
||||
scaffold's blue-grey because this app's own default ramp is
|
||||
black: a band of #1B2636 above and below it reads as the app
|
||||
failing to fill the screen. -->
|
||||
<color name="wails_background">#000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
<color name="black">#FF000000</color>
|
||||
</resources>
|
||||
|
||||
Reference in New Issue
Block a user