Skip to content
Open
Show file tree
Hide file tree
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 @@ -54,6 +54,7 @@ public class CapConfig {
private String errorPath;
private boolean zoomableWebView = false;
private boolean resolveServiceWorkerRequests = true;
private String adjustMarginsForEdgeToEdge = "auto";

// Embedded
private String startPath;
Expand Down Expand Up @@ -286,6 +287,7 @@ private void deserializeConfig(@Nullable Context context) {
webContentsDebuggingEnabled = JSONUtils.getBoolean(configJSON, "android.webContentsDebuggingEnabled", isDebug);
zoomableWebView = JSONUtils.getBoolean(configJSON, "android.zoomEnabled", JSONUtils.getBoolean(configJSON, "zoomEnabled", false));
resolveServiceWorkerRequests = JSONUtils.getBoolean(configJSON, "android.resolveServiceWorkerRequests", true);
adjustMarginsForEdgeToEdge = JSONUtils.getString(configJSON, "android.adjustMarginsForEdgeToEdge", "auto");

String logBehavior = JSONUtils.getString(
configJSON,
Expand Down Expand Up @@ -582,6 +584,7 @@ public static class Builder {
private int minHuaweiWebViewVersion = DEFAULT_HUAWEI_WEBVIEW_VERSION;
private boolean zoomableWebView = false;
private boolean resolveServiceWorkerRequests = true;
private String adjustMarginsForEdgeToEdge = "auto";

// Embedded
private String startPath = null;
Expand Down
157 changes: 29 additions & 128 deletions android/capacitor/src/main/java/com/getcapacitor/plugin/SystemBars.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
package com.getcapacitor.plugin;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.webkit.JavascriptInterface;
import android.webkit.WebView;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowCompat;
Expand All @@ -20,9 +13,10 @@
import com.getcapacitor.Plugin;
import com.getcapacitor.PluginCall;
import com.getcapacitor.PluginMethod;
import com.getcapacitor.WebViewListener;
import com.getcapacitor.annotation.CapacitorPlugin;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@CapacitorPlugin
public class SystemBars extends Plugin {
Expand All @@ -33,9 +27,6 @@ public class SystemBars extends Plugin {
static final String BAR_STATUS_BAR = "StatusBar";
static final String BAR_GESTURE_BAR = "NavigationBar";

static final String INSETS_HANDLING_CSS = "css";
static final String INSETS_HANDLING_DISABLE = "disable";

static final String viewportMetaJSFunction = """
function capacitorSystemBarsCheckMetaViewport() {
const meta = document.querySelectorAll("meta[name=viewport]");
Expand All @@ -46,55 +37,42 @@ function capacitorSystemBarsCheckMetaViewport() {
const metaContent = meta[meta.length - 1].content;
return metaContent.includes("viewport-fit=cover");
}

capacitorSystemBarsCheckMetaViewport();
""";

private boolean insetHandlingEnabled = true;
private boolean hasViewportCover = false;

private String currentStyle = STYLE_DEFAULT;

@Override
public void load() {
getBridge().getWebView().addJavascriptInterface(this, "CapacitorSystemBarsAndroidInterface");
super.load();

initSystemBars();
}

@Override
protected void handleOnStart() {
super.handleOnStart();

this.getBridge().addWebViewListener(
new WebViewListener() {
@Override
public void onPageCommitVisible(WebView view, String url) {
super.onPageCommitVisible(view, url);
getBridge().getWebView().requestApplyInsets();
}
}
);
}
private boolean hasFixedWebView() {
PackageInfo packageInfo = WebViewCompat.getCurrentWebViewPackage(bridge.getContext());
Pattern pattern = Pattern.compile("(\\d+)");
Matcher matcher = pattern.matcher(packageInfo.versionName);

@Override
protected void handleOnConfigurationChanged(Configuration newConfig) {
super.handleOnConfigurationChanged(newConfig);
if (!matcher.find()) {
return false;
}

setStyle(currentStyle, "");
String majorVersionStr = matcher.group(0);
int majorVersion = Integer.parseInt(majorVersionStr);

return majorVersion >= 140;
}

private void initSystemBars() {
String style = getConfig().getString("style", STYLE_DEFAULT).toUpperCase(Locale.US);
boolean hidden = getConfig().getBoolean("hidden", false);
boolean disableCSSInsets = getConfig().getBoolean("disableInsets", false);

String insetsHandling = getConfig().getString("insetsHandling", "css");
if (insetsHandling.equals(INSETS_HANDLING_DISABLE)) {
insetHandlingEnabled = false;
}

initWindowInsetsListener();
initSafeAreaInsets();
this.bridge.getWebView().evaluateJavascript(viewportMetaJSFunction, (res) -> {
boolean hasMetaViewportCover = res.equals("true");
if (!disableCSSInsets) {
setupSafeAreaInsets(this.hasFixedWebView(), hasMetaViewportCover);
}
});

getBridge().executeOnMainThread(() -> {
setStyle(style, "");
Expand Down Expand Up @@ -138,72 +116,17 @@ public void setAnimation(final PluginCall call) {
call.resolve();
}

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

getBridge().getWebView().requestApplyInsets();
});
});
}

private Insets calcSafeAreaInsets(WindowInsetsCompat insets) {
Insets safeArea = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
if (insets.isVisible(WindowInsetsCompat.Type.ime())) {
return Insets.of(safeArea.left, safeArea.top, safeArea.right, 0);
}
return Insets.of(safeArea.left, safeArea.top, safeArea.right, safeArea.bottom);
}

private void initSafeAreaInsets() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && insetHandlingEnabled) {
View v = (View) this.getBridge().getWebView().getParent();
WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(v);
if (insets != null) {
Insets safeAreaInsets = calcSafeAreaInsets(insets);
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
private void setupSafeAreaInsets(boolean hasFixedWebView, boolean hasMetaViewportCover) {
ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> {
if (hasFixedWebView && hasMetaViewportCover) {
return insets;
}
}
}

private void initWindowInsetsListener() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.VANILLA_ICE_CREAM && insetHandlingEnabled) {
ViewCompat.setOnApplyWindowInsetsListener((View) getBridge().getWebView().getParent(), (v, insets) -> {
boolean hasBrokenWebViewVersion = getWebViewMajorVersion() <= 139;

if (hasViewportCover) {
Insets safeAreaInsets = calcSafeAreaInsets(insets);
injectSafeAreaCSS(safeAreaInsets.top, safeAreaInsets.right, safeAreaInsets.bottom, safeAreaInsets.left);
}

if (hasBrokenWebViewVersion) {
if (hasViewportCover && v.hasWindowFocus() && v.isShown()) {
boolean keyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
if (keyboardVisible) {
Insets imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime());
setViewMargins(v, Insets.of(0, 0, 0, imeInsets.bottom));
} else {
setViewMargins(v, Insets.NONE);
}

return WindowInsetsCompat.CONSUMED;
}
}
Insets safeArea = insets.getInsets(WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout());
injectSafeAreaCSS(safeArea.top, safeArea.right, safeArea.bottom, safeArea.left);

return insets;
});
}
}

private void setViewMargins(View v, Insets insets) {
ViewGroup.MarginLayoutParams mlp = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
mlp.leftMargin = insets.left;
mlp.bottomMargin = insets.bottom;
mlp.rightMargin = insets.right;
mlp.topMargin = insets.top;
v.setLayoutParams(mlp);
return WindowInsetsCompat.CONSUMED;
});
}

private void injectSafeAreaCSS(int top, int right, int bottom, int left) {
Expand Down Expand Up @@ -239,8 +162,6 @@ private void injectSafeAreaCSS(int top, int right, int bottom, int left) {
}

private void setStyle(String style, String bar) {
currentStyle = style;

if (style.equals(STYLE_DEFAULT)) {
style = getStyleForTheme();
}
Expand All @@ -254,8 +175,6 @@ private void setStyle(String style, String bar) {
if (bar.isEmpty() || bar.equals(BAR_GESTURE_BAR)) {
windowInsetsControllerCompat.setAppearanceLightNavigationBars(!style.equals(STYLE_DARK));
}

getActivity().getWindow().getDecorView().setBackgroundColor(getThemeColor(getContext(), android.R.attr.windowBackground));
}

private void setHidden(boolean hide, String bar) {
Expand Down Expand Up @@ -287,22 +206,4 @@ private String getStyleForTheme() {
}
return STYLE_DARK;
}

public int getThemeColor(Context context, int attrRes) {
TypedValue typedValue = new TypedValue();

Resources.Theme theme = context.getTheme();
theme.resolveAttribute(attrRes, typedValue, true);
return typedValue.data;
}

private Integer getWebViewMajorVersion() {
PackageInfo info = WebViewCompat.getCurrentWebViewPackage(getContext());
if (info != null && info.versionName != null) {
String[] versionSegments = info.versionName.split("\\.");
return Integer.valueOf(versionSegments[0]);
}

return 0;
}
}
12 changes: 3 additions & 9 deletions cli/src/declarations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -707,17 +707,11 @@ export interface PluginsConfig {
*/
SystemBars?: {
/**
* Specifies how to handle problematic insets on Android.
* Disables the injection of device css insets into the web view.
*
* This option is only supported on Android.
*
* `css` = Injects CSS variables (`--safe-area-inset-*`) containing correct safe area inset values into the webview.
*
* `disable` = Disable all inset handling.
*
* @default "css"
* @default false
*/
insetsHandling?: 'css' | 'disable';
disableInsets?: boolean;
/**
* The style of the text and icons of the system bars.
*
Expand Down
2 changes: 1 addition & 1 deletion core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"build": "npm run clean && npm run docgen && npm run transpile && npm run rollup",
"build:nativebridge": "tsc native-bridge.ts --target es2017 --moduleResolution node --outDir build && rollup --config rollup.bridge.config.js",
"clean": "rimraf dist",
"docgen": "docgen --api CapacitorCookiesPlugin --output-readme cookies.md && docgen --api CapacitorHttpPlugin --output-readme http.md && docgen --api SystemBarsPlugin --output-readme system-bars.md",
"docgen": "docgen --api CapacitorCookiesPlugin --output-readme cookies.md && docgen --api CapacitorHttpPlugin --output-readme http.md && docgen --api SystemBarsPlugin --output-readme systembars.md",
"prepublishOnly": "npm run build",
"rollup": "rollup --config rollup.config.js",
"transpile": "tsc",
Expand Down
4 changes: 2 additions & 2 deletions core/src/core-plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,14 +617,14 @@ export interface SystemBarsPlugin {
*
* @since 8.0.0
*/
show(options?: SystemBarsVisibilityOptions): Promise<void>;
show(options: SystemBarsVisibilityOptions): Promise<void>;

/**
* Hide the system bars.
*
* @since 8.0.0
*/
hide(options?: SystemBarsVisibilityOptions): Promise<void>;
hide(options: SystemBarsVisibilityOptions): Promise<void>;

/**
* Set the animation to use when showing / hiding the status bar.
Expand Down
Loading