From a2195e1fc6242d01ce043acd98fa053d2e94f635 Mon Sep 17 00:00:00 2001 From: Norman Breau Date: Sat, 29 Dec 2018 01:21:45 -0400 Subject: [PATCH 1/4] CB-13300: (android) Fixed keyboard from overlapping content while statusbar is in overlay mode --- plugin.xml | 1 + src/android/StatusBar.java | 3 + src/android/StatusBarViewHelper.java | 84 ++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 src/android/StatusBarViewHelper.java diff --git a/plugin.xml b/plugin.xml index ea5a7900..fc4fe8bc 100644 --- a/plugin.xml +++ b/plugin.xml @@ -38,6 +38,7 @@ + diff --git a/src/android/StatusBar.java b/src/android/StatusBar.java index 714c30e8..1490bdab 100644 --- a/src/android/StatusBar.java +++ b/src/android/StatusBar.java @@ -54,6 +54,9 @@ public void initialize(final CordovaInterface cordova, CordovaWebView webView) { this.cordova.getActivity().runOnUiThread(new Runnable() { @Override public void run() { + // CB-13300: This corrects keyboard behaviour when overlaysWebView is true + StatusBarViewHelper.assistActivity(cordova.getActivity()); + // Clear flag FLAG_FORCE_NOT_FULLSCREEN which is set initially // by the Cordova. Window window = cordova.getActivity().getWindow(); diff --git a/src/android/StatusBarViewHelper.java b/src/android/StatusBarViewHelper.java new file mode 100644 index 00000000..8224a0e0 --- /dev/null +++ b/src/android/StatusBarViewHelper.java @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * +*/ + +package org.apache.cordova.statusbar; + +import android.app.Activity; +import android.graphics.Rect; +import android.util.Log; +import android.view.View; +import android.view.ViewTreeObserver; +import android.widget.FrameLayout; + +/* + Issue ID: CB-13300 + This happens when the status bar is in overlay mode, because to get transparent + status bars in Android, you need to use a full screen layout, which prevents + the webview from adjusting automatically. + + This class watches for layout changes and resizes the webview based on + status bar, navigation bar (android phones without physical navigation buttons), + and keyboard states to prevent the keyboard from overlapping content when shown. + */ +public class StatusBarViewHelper { + private View mChildOfContent; + private int usableHeightPrevious; + private FrameLayout.LayoutParams frameLayoutParams; + private Activity activity; + + static void assistActivity(Activity activity) { + new StatusBarViewHelper(activity); + } + + private StatusBarViewHelper(Activity a) { + activity = a; + FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); + mChildOfContent = content.getChildAt(0); + + mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + public void onGlobalLayout() { + possiblyResizeChildOfContent(); + } + }); + + frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams(); + } + + private void possiblyResizeChildOfContent() { + int usableHeightNow = computeUsableHeight(); + if (usableHeightNow != usableHeightPrevious) { + frameLayoutParams.height = usableHeightNow; + mChildOfContent.requestLayout(); + usableHeightPrevious = usableHeightNow; + } + } + + private int computeUsableHeight() { + Rect r = new Rect(); + mChildOfContent.getWindowVisibleDisplayFrame(r); + int uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility(); + boolean isFullscreen = ((uiOptions | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == uiOptions); + + //If not fullscreen, then we have to take the status bar into consideration (represented by r.top) + //r.bottom defines the keyboard, or navigation bar, or both. + + return isFullscreen ? r.bottom : r.bottom - r.top; + } +} From dde57f9a1fd2aa0f1959627cb6a4794652b0dcf6 Mon Sep 17 00:00:00 2001 From: Armand Niculescu Date: Thu, 3 Oct 2019 16:18:28 +0300 Subject: [PATCH 2/4] Add cutout support --- src/android/StatusBarViewHelper.java | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/android/StatusBarViewHelper.java b/src/android/StatusBarViewHelper.java index 8224a0e0..e4a19bd2 100644 --- a/src/android/StatusBarViewHelper.java +++ b/src/android/StatusBarViewHelper.java @@ -26,6 +26,7 @@ import android.view.View; import android.view.ViewTreeObserver; import android.widget.FrameLayout; +import android.view.DisplayCutout; /* Issue ID: CB-13300 @@ -76,9 +77,28 @@ private int computeUsableHeight() { int uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility(); boolean isFullscreen = ((uiOptions | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == uiOptions); - //If not fullscreen, then we have to take the status bar into consideration (represented by r.top) - //r.bottom defines the keyboard, or navigation bar, or both. - - return isFullscreen ? r.bottom : r.bottom - r.top; + int cutoutTop = 0; + int cutoutBottom = 0; + + // On devices with screen cutouts, this code will think that screen is available, when in reality it is not. + // This piece gets the cutout information so that we can correct the computeUsableHeight + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { + DisplayCutout cutout = activity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout(); + if (cutout !=null) { + cutoutTop = cutout.getSafeInsetTop(); + cutoutBottom = cutout.getSafeInsetBottom(); + } + } + + int usableHeight = r.bottom - cutoutTop - cutoutBottom; + + //If not fullscreen, then we have to take the status bar into consideration (represented by r.top) + //r.bottom defines the keyboard, or navigation bar, or both. + if (isFullscreen) + { + usableHeight = usableHeight - r.top; + } + + return usableHeight; } } From 028a591121a578a0ca3d51405f11bf440ff6eb02 Mon Sep 17 00:00:00 2001 From: Norman Breau Date: Mon, 14 Oct 2019 14:57:12 -0300 Subject: [PATCH 3/4] Fix webview resizing when statusbar is hidden --- src/android/StatusBar.java | 9 ++- src/android/StatusBarViewHelper.java | 83 +++++++++++++++------------- 2 files changed, 52 insertions(+), 40 deletions(-) diff --git a/src/android/StatusBar.java b/src/android/StatusBar.java index 1490bdab..e2354a0c 100644 --- a/src/android/StatusBar.java +++ b/src/android/StatusBar.java @@ -51,11 +51,14 @@ public void initialize(final CordovaInterface cordova, CordovaWebView webView) { LOG.v(TAG, "StatusBar: initialization"); super.initialize(cordova, webView); + StatusBar statusbar = this; + this.cordova.getActivity().runOnUiThread(new Runnable() { @Override public void run() { - // CB-13300: This corrects keyboard behaviour when overlaysWebView is true - StatusBarViewHelper.assistActivity(cordova.getActivity()); + //https://github.com/apache/cordova-plugin-statusbar/issues/110 + //This corrects keyboard behaviour when overlaysWebView is true + StatusBarViewHelper.assist(cordova.getActivity(), statusbar); // Clear flag FLAG_FORCE_NOT_FULLSCREEN which is set initially // by the Cordova. @@ -68,7 +71,7 @@ public void run() { // Read 'StatusBarStyle' from config.xml, default is 'lightcontent'. setStatusBarStyle(preferences.getString("StatusBarStyle", "lightcontent")); } - }); + } } /** diff --git a/src/android/StatusBarViewHelper.java b/src/android/StatusBarViewHelper.java index 8224a0e0..b2ded139 100644 --- a/src/android/StatusBarViewHelper.java +++ b/src/android/StatusBarViewHelper.java @@ -1,54 +1,35 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - * -*/ - package org.apache.cordova.statusbar; import android.app.Activity; import android.graphics.Rect; -import android.util.Log; +import android.view.DisplayCutout; import android.view.View; import android.view.ViewTreeObserver; import android.widget.FrameLayout; -/* - Issue ID: CB-13300 - This happens when the status bar is in overlay mode, because to get transparent - status bars in Android, you need to use a full screen layout, which prevents - the webview from adjusting automatically. - - This class watches for layout changes and resizes the webview based on - status bar, navigation bar (android phones without physical navigation buttons), - and keyboard states to prevent the keyboard from overlapping content when shown. - */ public class StatusBarViewHelper { + + // For more information, see https://issuetracker.google.com/issues/36911528 + // To use this class, simply invoke assistActivity() on an Activity that already has its content view set. + + //This solution was based off of + //https://stackoverflow.com/questions/7417123/android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible/19494006#answer-42261118 + private View mChildOfContent; private int usableHeightPrevious; private FrameLayout.LayoutParams frameLayoutParams; private Activity activity; + private StatusBar statusbar; + private static final String TAG = "StatusBarViewHelper"; - static void assistActivity(Activity activity) { - new StatusBarViewHelper(activity); + static void assist(Activity activity, StatusBar statusbar) { + new StatusBarViewHelper(activity, statusbar); } - private StatusBarViewHelper(Activity a) { + private StatusBarViewHelper(Activity a, StatusBar b) { activity = a; + statusbar = b; + FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content); mChildOfContent = content.getChildAt(0); @@ -70,15 +51,43 @@ private void possiblyResizeChildOfContent() { } } + private boolean _isStatusBarVisible() { + return statusbar.isVisible(); + } + private int computeUsableHeight() { Rect r = new Rect(); mChildOfContent.getWindowVisibleDisplayFrame(r); int uiOptions = activity.getWindow().getDecorView().getSystemUiVisibility(); boolean isFullscreen = ((uiOptions | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == uiOptions); + boolean isStatusBarVisible = this._isStatusBarVisible(); - //If not fullscreen, then we have to take the status bar into consideration (represented by r.top) - //r.bottom defines the keyboard, or navigation bar, or both. + int usableHeight = r.bottom; + + if (isStatusBarVisible) { + //If not fullscreen, then we have to take the status bar into consideration (represented by r.top) + //r.bottom defines the keyboard, or navigation bar, or both. + if (!isFullscreen) { + usableHeight = usableHeight - r.top; + } + } + else { + int cutoutTop = 0; + int cutoutBottom = 0; + + // On devices with screen cutouts, this code will think that screen is available, when in reality it is not. + // This piece gets the cutout information so that we can correct the computeUsableHeight + if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { + DisplayCutout cutout = activity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout(); + if (cutout != null) { + cutoutTop = cutout.getSafeInsetTop(); + cutoutBottom = cutout.getSafeInsetBottom(); + } + } + + usableHeight = usableHeight - cutoutTop - cutoutBottom; + } - return isFullscreen ? r.bottom : r.bottom - r.top; + return usableHeight; } } From 430e7df32c0937d46e3a596b37ecbd50a42cb9c3 Mon Sep 17 00:00:00 2001 From: Norman Breau Date: Tue, 15 Oct 2019 23:21:48 -0300 Subject: [PATCH 4/4] handle cutouts when statusbar is hidden fixed syntax error --- src/android/StatusBar.java | 12 +++++++++++- src/android/StatusBarViewHelper.java | 28 ++++------------------------ 2 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/android/StatusBar.java b/src/android/StatusBar.java index e2354a0c..3b51b3b0 100644 --- a/src/android/StatusBar.java +++ b/src/android/StatusBar.java @@ -39,6 +39,8 @@ public class StatusBar extends CordovaPlugin { private static final String TAG = "StatusBar"; + private boolean _isVisible = true; + /** * Sets the context of the Command. This can then be used to do things like * get file paths associated with the Activity. @@ -71,7 +73,11 @@ public void run() { // Read 'StatusBarStyle' from config.xml, default is 'lightcontent'. setStatusBarStyle(preferences.getString("StatusBarStyle", "lightcontent")); } - } + }); + } + + public boolean isVisible() { + return _isVisible; } /** @@ -111,6 +117,8 @@ public void run() { // CB-11197 We still need to update LayoutParams to force status bar // to be hidden when entering e.g. text fields window.clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + + _isVisible = true; } }); return true; @@ -133,6 +141,8 @@ public void run() { // CB-11197 We still need to update LayoutParams to force status bar // to be hidden when entering e.g. text fields window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN); + + _isVisible = false; } }); return true; diff --git a/src/android/StatusBarViewHelper.java b/src/android/StatusBarViewHelper.java index 0f898aeb..8d85f450 100644 --- a/src/android/StatusBarViewHelper.java +++ b/src/android/StatusBarViewHelper.java @@ -2,11 +2,9 @@ import android.app.Activity; import android.graphics.Rect; -import android.view.DisplayCutout; import android.view.View; import android.view.ViewTreeObserver; import android.widget.FrameLayout; -import android.view.DisplayCutout; public class StatusBarViewHelper { @@ -65,28 +63,10 @@ private int computeUsableHeight() { int usableHeight = r.bottom; - if (isStatusBarVisible) { - //If not fullscreen, then we have to take the status bar into consideration (represented by r.top) - //r.bottom defines the keyboard, or navigation bar, or both. - if (!isFullscreen) { - usableHeight = usableHeight - r.top; - } - } - else { - int cutoutTop = 0; - int cutoutBottom = 0; - - // On devices with screen cutouts, this code will think that screen is available, when in reality it is not. - // This piece gets the cutout information so that we can correct the computeUsableHeight - if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { - DisplayCutout cutout = activity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout(); - if (cutout != null) { - cutoutTop = cutout.getSafeInsetTop(); - cutoutBottom = cutout.getSafeInsetBottom(); - } - } - - usableHeight = usableHeight - cutoutTop - cutoutBottom; + // This handles both overlayed status bars and reserved spaces for cutouts when the + // status bar is hidden + if (!isFullscreen || (isFullscreen && !isStatusBarVisible)) { + usableHeight = usableHeight - r.top; } return usableHeight;