diff --git a/gleap/src/main/AndroidManifest.xml b/gleap/src/main/AndroidManifest.xml index 53dc437..7018e74 100644 --- a/gleap/src/main/AndroidManifest.xml +++ b/gleap/src/main/AndroidManifest.xml @@ -7,7 +7,8 @@ diff --git a/gleap/src/main/java/io/gleap/GleapMainActivity.java b/gleap/src/main/java/io/gleap/GleapMainActivity.java index cc06ae7..eee795c 100644 --- a/gleap/src/main/java/io/gleap/GleapMainActivity.java +++ b/gleap/src/main/java/io/gleap/GleapMainActivity.java @@ -7,6 +7,7 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.content.res.Configuration; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; @@ -197,6 +198,11 @@ public void handleOnBackPressed() { this.requestWindowFeature(Window.FEATURE_NO_TITLE); try { + // SOFT_INPUT_ADJUST_RESIZE is deprecated on API 30+ but still honoured + // for apps that target SDK < 35 (this SDK targets 33). Setting it + // unconditionally ensures the window shrinks for the keyboard on every + // device – including Android 16 / Samsung One UI 8.0 – so the WebView + // content remains visible without needing a separate IME-insets handler. getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); if (getSupportActionBar() != null) { getSupportActionBar().hide(); @@ -214,25 +220,24 @@ public void handleOnBackPressed() { final FrameLayout webViewContainer = findViewById(R.id.webview_container); + // Handle system-bar insets (status bar, navigation bar) so the + // WebView content is not drawn behind them. We intentionally + // do NOT include IME insets here: the keyboard resize is fully + // handled by SOFT_INPUT_ADJUST_RESIZE which shrinks the window + // itself. Adding IME padding on top of that would double-shift + // the content -- the root cause of the blank-screen-on-keyboard + // bug on Android 16 / Samsung One UI 8.0. ViewCompat.setOnApplyWindowInsetsListener(webViewContainer, (view, insets) -> { - // status + navigation bars (stable system bars) Insets bars = insets.getInsets(WindowInsetsCompat.Type.systemBars()); - // on Android 11+ this is the real keyboard height; - // on older versions it’s 0 – but then the IME is reported as a system‑bar inset - Insets ime = insets.getInsets(WindowInsetsCompat.Type.ime()); - - int topInset = bars.top; // keep the status‑bar height - int bottomInset = Math.max(bars.bottom, ime.bottom); // choose the larger of nav‑bar or IME - view.setPadding(view.getPaddingLeft(), - topInset, + bars.top, view.getPaddingRight(), - bottomInset); + bars.bottom); - return insets; // DON’T consume – let child views see the same insets + return insets; }); int backgroundColor = Color.parseColor(GleapConfig.getInstance().getBackgroundColor()); @@ -285,9 +290,19 @@ public void invoke() { @Override public void run() { url += GleapURLGenerator.generateURL(); - initBrowser(); if (savedInstanceState != null) { - webView.restoreState(savedInstanceState); + // Activity was recreated (e.g. process killed in background). + // Set up WebView clients/settings but skip loading a new URL; + // restoreState will navigate back to the previous page. + initBrowserSettings(); + android.webkit.WebBackForwardList restored = webView.restoreState(savedInstanceState); + if (restored == null) { + // restoreState failed — fall back to a full reload. + webView.loadUrl(url); + } + } else { + // Fresh start — initialise everything and load the URL. + initBrowser(); } } }); @@ -305,7 +320,20 @@ protected void onSaveInstanceState(Bundle outState) { @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); - webView.restoreState(savedInstanceState); + // Do NOT call webView.restoreState() here. The posted runnable in + // onCreate already handles restoration after the WebView clients are + // wired up via initBrowserSettings(). Calling restoreState() here + // would consume the Bundle's WebView state before that runnable + // executes, forcing an unnecessary full URL reload every time. + } + + @Override + public void onConfigurationChanged(@NonNull Configuration newConfig) { + super.onConfigurationChanged(newConfig); + // Intentionally empty — we declared the relevant configChanges in the + // manifest so the system delivers them here instead of recreating the + // activity. This keeps the WebView (and its chat state) alive when the + // soft keyboard appears/disappears or the screen layout changes. } @Override @@ -359,7 +387,12 @@ protected void onDestroy() { super.onDestroy(); } - private void initBrowser() { + /** + * Configure WebView settings, clients and JS bridge without loading a URL. + * Call this when restoring from saved state so the WebView is properly + * wired up before {@code restoreState()} navigates to the previous page. + */ + private void initBrowserSettings() { WebSettings settings = webView.getSettings(); settings.setJavaScriptEnabled(true); settings.setDomStorageEnabled(true); @@ -443,10 +476,16 @@ public boolean onShowFileChooser(WebView webView, ValueCallback filePathC } } }); - webView.loadUrl(url); webView.setVisibility(View.INVISIBLE); - settings.setUseWideViewPort(true); - settings.setLoadWithOverviewMode(true); + } + + /** + * Full browser initialisation: configure settings, clients and load the URL. + * Used on a fresh start (no saved instance state). + */ + private void initBrowser() { + initBrowserSettings(); + webView.loadUrl(url); } public void askForPermission(String origin, String androidPermission, String webkitPermission, int requestCode) {