Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ function capacitorSystemBarsCheckMetaViewport() {
private String currentStatusBarStyle = STYLE_DEFAULT;
private String currentGestureBarStyle = STYLE_DEFAULT;

private boolean navBarVisible = true;

@Override
public void load() {
getBridge().getWebView().addJavascriptInterface(this, "CapacitorSystemBarsAndroidInterface");
Expand All @@ -76,7 +78,15 @@ protected void handleOnStart() {
@Override
public void onPageCommitVisible(WebView view, String url) {
super.onPageCommitVisible(view, url);
getBridge().getWebView().requestApplyInsets();
View parentView = (View) getBridge().getWebView().getParent();
ViewCompat.requestApplyInsets(parentView);
if (insetHandlingEnabled) {
WindowInsetsCompat rootInsets = ViewCompat.getRootWindowInsets(parentView);
if (rootInsets != null) {
Insets safeArea = calcSafeAreaInsets(rootInsets);
injectSafeAreaCSS(safeArea.top, safeArea.right, safeArea.bottom, safeArea.left);
}
}
}
}
);
Expand All @@ -103,8 +113,17 @@ private void initSystemBars() {
initSafeAreaCSSVariables();

getBridge().executeOnMainThread(() -> {
Window window = getActivity().getWindow();
WindowCompat.setDecorFitsSystemWindows(window, false);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
window.setNavigationBarColor(android.graphics.Color.TRANSPARENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.setNavigationBarContrastEnforced(false);
}
}
setStyle(style, "");
setHidden(hidden, "");
ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
});
}

Expand Down Expand Up @@ -144,27 +163,64 @@ public void setAnimation(final PluginCall call) {
call.resolve();
}

@Override
protected void handleOnResume() {
super.handleOnResume();
getBridge().executeOnMainThread(() -> {
Window window = getActivity().getWindow();
WindowCompat.setDecorFitsSystemWindows(window, false);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) {
window.setNavigationBarColor(android.graphics.Color.TRANSPARENT);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
window.setNavigationBarContrastEnforced(false);
}
}
setStyle(currentGestureBarStyle, BAR_GESTURE_BAR);
setStyle(currentStatusBarStyle, BAR_STATUS_BAR);
ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
});
}

@JavascriptInterface
public void onDOMReady() {
getActivity().runOnUiThread(() -> {
this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> {
hasViewportCover = res.equals("true");

getBridge().getWebView().requestApplyInsets();
ViewCompat.requestApplyInsets((View) getBridge().getWebView().getParent());
});
});
}

private Insets calcSafeAreaInsets(WindowInsetsCompat insets) {
Insets safeArea = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
if (insets.isVisible(WindowInsetsCompat.Type.ime())) {

int bottom = safeArea.bottom;

if (bottom == 0
&& Build.VERSION.SDK_INT < Build.VERSION_CODES.R
&& safeArea.left == 0 && safeArea.right == 0) { // skip if nav bar is on a side (landscape)
bottom = getNavBarHeightFromResources();
}

boolean imeVisible = Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
? insets.isVisible(WindowInsetsCompat.Type.ime())
: insets.getInsets(WindowInsetsCompat.Type.ime()).bottom > 0;

if (imeVisible) {
return Insets.of(safeArea.left, safeArea.top, safeArea.right, 0);
}
return Insets.of(safeArea.left, safeArea.top, safeArea.right, safeArea.bottom);
return Insets.of(safeArea.left, safeArea.top, safeArea.right, bottom);
}

private int getNavBarHeightFromResources() {
if (!navBarVisible) return 0;
android.content.res.Resources res = getActivity().getResources();
int heightId = res.getIdentifier("navigation_bar_height", "dimen", "android");
return heightId > 0 ? res.getDimensionPixelSize(heightId) : 0;
}

private void initSafeAreaCSSVariables() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && insetHandlingEnabled) {
if (insetHandlingEnabled) {
View v = (View) this.getBridge().getWebView().getParent();
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(v);
if (insets != null) {
Expand All @@ -176,6 +232,10 @@ private void initSafeAreaCSSVariables() {

private void initWindowInsetsListener() {
ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> {
// getRootWindowInsets() bypasses AppCompat's intermediate view consuming the bottom inset on API < 30
WindowInsetsCompat rawInsets = ViewCompat.getRootWindowInsets(v);
WindowInsetsCompat safeAreaSource = (rawInsets != null) ? rawInsets : insets;

boolean shouldPassthroughInsets = getWebViewMajorVersion() >= WEBVIEW_VERSION_WITH_SAFE_AREA_FIX && hasViewportCover;

Insets systemBarsInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
Expand All @@ -186,8 +246,8 @@ private void initWindowInsetsListener() {
// We need to correct for a possible shown IME
v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) {
Insets safeAreaInsets = calcSafeAreaInsets(insets);
if (hasViewportCover && insetHandlingEnabled) {
Insets safeAreaInsets = calcSafeAreaInsets(safeAreaSource);
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
}

Expand All @@ -204,15 +264,7 @@ private void initWindowInsetsListener() {
.build();
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM) {
// We need to correct for a possible shown IME
v.setPadding(
systemBarsInsets.left,
systemBarsInsets.top,
systemBarsInsets.right,
keyboardVisible ? imeInsets.bottom : systemBarsInsets.bottom
);
}
v.setPadding(0, 0, 0, keyboardVisible ? imeInsets.bottom : 0);

// Returning `WindowInsetsCompat.CONSUMED` breaks recalculation of safe area insets
// So we have to explicitly set insets to `0`
Expand All @@ -221,8 +273,8 @@ private void initWindowInsetsListener() {
.setInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout(), Insets.of(0, 0, 0, 0))
.build();

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && hasViewportCover && insetHandlingEnabled) {
Insets safeAreaInsets = calcSafeAreaInsets(newInsets);
if (insetHandlingEnabled) {
Insets safeAreaInsets = calcSafeAreaInsets(safeAreaSource);
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
}

Expand Down Expand Up @@ -263,19 +315,20 @@ private void injectSafeAreaCSS(int top, int right, int bottom, int left) {
}

private void setStyle(String style, String bar) {
String requestedStyle = style;
if (style.equals(STYLE_DEFAULT)) {
style = getStyleForTheme();
}

Window window = getActivity().getWindow();
WindowInsetsControllerCompat windowInsetsControllerCompat = WindowCompat.getInsetsController(window, window.getDecorView());
if (bar.isEmpty() || bar.equals(BAR_STATUS_BAR)) {
currentStatusBarStyle = style;
currentStatusBarStyle = requestedStyle;
windowInsetsControllerCompat.setAppearanceLightStatusBars(!style.equals(STYLE_DARK));
}

if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
currentGestureBarStyle = style;
currentGestureBarStyle = requestedStyle;
windowInsetsControllerCompat.setAppearanceLightNavigationBars(!style.equals(STYLE_DARK));
}

Expand All @@ -292,6 +345,7 @@ private void setHidden(boolean hide, String bar) {
}
if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
windowInsetsControllerCompat.hide(WindowInsetsCompat.Type.navigationBars());
navBarVisible = false;
}
return;
}
Expand All @@ -301,6 +355,7 @@ private void setHidden(boolean hide, String bar) {
}
if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
windowInsetsControllerCompat.show(WindowInsetsCompat.Type.navigationBars());
navBarVisible = true;
}
}

Expand Down