ionic-team / capacitor

Build cross-platform Native Progressive Web Apps for iOS, Android, and the Web ⚑️

Home Page:https://capacitorjs.com

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

bug(android): overlaying status bar does not affect safe area insets

diachedelic opened this issue Β· comments

Bug Report

Capacitor Version

npx cap doctor output:

$ npx cap doctor
πŸ’Š Capacitor Doctor πŸ’Š

Latest Dependencies:

@capacitor/cli: 2.0.1

@capacitor/core: 2.0.1

@capacitor/android: 2.0.1

@capacitor/electron: 2.0.1

@capacitor/ios: 2.0.1

Installed Dependencies:

@capacitor/electron not installed

@capacitor/cli 2.0.1

@capacitor/core 2.0.1

@capacitor/android 2.0.1

@capacitor/ios 2.0.1

[success] Android looking great! πŸ‘Œ
Found 10 Capacitor plugins for ios:
@mauron85/cordova-plugin-background-geolocation (3.1.0)
capacitor-data-storage-sqlite (2.0.0-1)
capacitor-voice-recorder (0.0.9)
cordova-plugin-android-permissions (1.0.2)
cordova-plugin-app-version (0.1.9)
cordova-plugin-file (6.0.2)
cordova-plugin-file-transfer (1.7.1)
cordova-plugin-screen-orientation (3.0.2)
cordova-plugin-whitelist (1.3.4)
es6-promise-plugin (4.2.2)
[success] iOS looking great! πŸ‘Œ

Affected Platform(s)

  • Android
  • iOS
  • Electron
  • Web

Current Behavior

On iOS, I leave space for the iPhone X notch & iPhone 6 status bar using <meta name="viewport" content="viewport-fit=cover" /> along with env(safe-area-inset-top), as described in #2100 .

I would love to do the same thing in Android, however it appears that overlaying the status bar via StatusBar.setOverlaysWebView({ overlay: true }) does not affect env(safe-area-inset-top) in the CSS. env(safe-area-inset-*) has officially been supported since Chrome 69 (announcement) though how extensively I don't know.

Expected Behavior

I would expect env(safe-area-inset-top) to include the height of the status bar if it is overlaid.

Reproduction Steps

    // Display content under transparent status bar (Android only)
    StatusBar.setOverlaysWebView({
      overlay: true
    });

Other Technical Details

npm --version output: 6.14.4

node --version output: v12.16.1

Other Information

If fixing env(safe-area-inset-top) is not possible, I would be satisfied being able to access the safe area insets via a Plugin, or injected CSS variables (e.g. --android-safe-area-inset-top or similar).

Looks like despite they implemented, it doesn't work, it always returns 0. so this is a Chromium bug, not a Capacitor bug.

For getting the safe areas, that seems to be possible on SDK 29+, so could be added in some plugin (Status Bar maybe?), so closing as feature request.

Issues tagged with feature request are closed but tracked forπŸ‘ reactions to gauge interest.

Hi there, do you have any information on this ? Thank you !

commented

Has anyone figured this out? Looking for how to overlay content behind the status bar (so the color of it matches the content), but push the content down so that it isn't actually behind the status bar. Seems like ion-toolbar needs the safe area padding on top but its always 0.

Same issue here! @Cyral have you been successful yet?

commented

@haschu Nope. Tons of apps do this, seems very common - but still haven't found a way with Capacitor/Ionic

Nothing yet?

Is there any workaround for this issue?

I had the same problem

I'm developing a plugin that sends the device insets, at the moment works only on android, and send only top, its for a emergency.
https://www.npmjs.com/package/capacitor-insets-plugin

const { StatusBar, InsetsPlugin } = Plugins;

StatusBar.setOverlaysWebView({ overlay: true });
StatusBar.setStyle({ style: StatusBarStyle.Dark });

InsetsPlugin.top()
    .then((resp: { value: number }) => {
        document.documentElement.style.setProperty('--top-inset', `${resp.value}px`);
    });

It's not settled yet?

coming back to this issue after a year. any idea what the recommended way to handle this issue is?

commented

Apparently it's actually an issue on the Android webview side. Tracking it here: https://bugs.chromium.org/p/chromium/issues/detail?id=1094366 Of course whenever this does get fixed there will be a ton of devices no longer receiving manufacturer updates that will never get it.

No workaround yet?

commented

I ended up making my own workaround but it's hacky and somewhat complicated.

In onCreate in your MainActivity, make the app draw behind the system status bar and nav bar: (and make them transparent)

    // Use a transparent status bar and nav bar, and place the window behind the status bar
    // and nav bar. Due to a chromium bug, we need to get the height of both bars
    // and add it to the safe area insets. The native plugin is used to get this info.
    // See https://bugs.chromium.org/p/chromium/issues/detail?id=1094366
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
      getWindow().setDecorFitsSystemWindows(false);
      getWindow().setStatusBarColor(0);
      getWindow().setNavigationBarColor(0);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
      // On older versions of android setDecorFitsSystemWindows doesn't exist yet, but it can
      // be emulated with flags.
      // It still must be P or greater, as that is the min version for getting the insets
      // through the native plugin.
      getWindow().getDecorView().setSystemUiVisibility(
              View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
                      View.SYSTEM_UI_FLAG_HIDE_NAVIGATION |
                      View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
      getWindow().setStatusBarColor(0);
      getWindow().setNavigationBarColor(0);
    }

Then I use a plugin to get the display insets:

    @PluginMethod
    public void getDisplayInsets(PluginCall call) {
        Activity activity = getBridge().getActivity();
        Window window = activity.getWindow();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            // See https://bugs.chromium.org/p/chromium/issues/detail?id=1094366, which is
            // the bug that makes getting the display inset necessary.
            final WindowInsets cutout = window.getDecorView().getRootWindowInsets();
            final float density = activity.getResources().getDisplayMetrics().density;

            JSObject ret = new JSObject();
            ret.put("top", cutout.getStableInsetTop() / density);
            ret.put("bottom", cutout.getStableInsetBottom() / density);
            call.resolve(ret);
        }
    }

And set the safe area to the insets upon startup in index.tsx: (Native is the name of the plugin I created for this)

    if (isPlatform('android')) {
        Native.getDisplayInsets().then(i => {
            const style = document.documentElement.style;
            style.setProperty('--ion-safe-area-top', i.top + 'px');
            style.setProperty('--ion-safe-area-bottom', i.bottom + 'px');
        });
    }

Lastly, ion-modals (without a top header) don't seem to respect the safe area, so I had to add the following to my CSS:

ion-modal {
  --ion-content-scroll-safe-area: var(--ion-safe-area-bottom, 0);
}

ion-content::part(scroll) {
  margin-bottom: calc(var(--ion-content-scroll-safe-area, 0));
}

Once the Chromium bug is fixed, only the CSS part should be needed. That part appears to be a bug in ionic.

I believe this also results in keyboard height getting calculated incorrectly on Android devices with a top notch.

@Cyral any chance you could share your plugin? (Edit: I found this plugin which seems to do what's required - https://github.com/capacitor-community/safe-area)

This has nothing to do with Safe Area, this should be handled with Android default behavior for as you can find here: https://github.com/razir/MoviesInsetsDemo. Safe area should be used only for the web, where a native approach should be used inside of the WebView. Both navigation and the status bar should use this Android API since Android manufacturers handle notches and navigation bars differently and they all use this Android standardized approach. Our team will contribute to Capacitor with this fix please feel free to review and merge.

I published a plugin, based on other peoples repos, to get the value of status bar, regardless if it's ionic bug or chromium.

https://github.com/owlsdepartment/capacitor-plugin-android-insets

this problem is easily solved by adding in app.module imports.

IonicModule.forRoot({_forceStatusbarPadding: true})

A demo in Cordova to be seen here

Thanks for the issue! This issue is being locked to prevent comments that are not relevant to the original issue. If this is still an issue with the latest version of Capacitor, please create a new issue and ensure the template is fully filled out.