Positioning Unity Admob Banner at the Bottom on a Device with a Display Cutout

Viewed 5247

I'm trying to position my ad banner (generated via the official Admob package for Unity) at the very bottom of the screen, but it doesn't seem to work very well with devices with a notch.

bv = new BannerView(adUnitId, adSize, AdPosition.Bottom);

This line of code positions the banner at the very bottom perfectly on notch-less devices (such as Pixel, Pixel XL, Pixel 2 & 2 XL), but look like this when the device have a notch at the top:

enter image description here

(The demo photo is taken from this Github Issue. Ignore the visible navigation bar.)

the space between the banner and the bottom of the screen is exactly the height of the notch. I tested it with multiple notch heights.

In order to position the banner at the very bottom, I think the best solution is to get the height of the notch and then use the following line of code:

new BannerView(adUnitId, adSize, 0, (int)ConvertPixelsToDP(Screen.height - adHeight + notchHeight));

But I couldn't find any way to get the cutout height. I tried using Screen and Display classes properties, but none of the ways helped me get the real resolution of the screen, including the notch, so I could subtract the safeArea height - and get the exact height of the notch.

Is there any way to get this real screen resolution? or any other possible solution to this problem?

2 Answers

From the main GitHub issue thread about this the AdMob Unity plugin developers stated "Will be prioritizing notch compatibility for the next release." back in October, indicating they are aware of the bug, and they posted on January 22nd saying "This work got pushed back but will be making it into the next release. No ETAs yet but should be soon." So who knows when the issue will ever get fixed.

But to anyone who is wrestling with this issue: I found a workaround. In my case it's a workaround for getting the ad to appear at the bottom of the screen, but I will also mention how you can probably get it to display at the top properly. I still hope for a release that just fixes this soon, but here is an option for those who don't want to wait around...

I discovered this workaround because I first started with the obvious solution: compute the y position manually via the obvious formula: int yDP = DensityIndependentPixels(Mathf.RoundToInt(Screen.safeArea.y + Screen.safeArea.height) - ScreenPixels(adSize.Height)); This would work, except for the fact that as of Unity 2018.3.5 safeArea always has 0, 0 for x and y, and while the width and height are usable to detect that there's a notch when compared to Display.main.systemHeight (although as far as I can tell, Screen.height == safeArea.height and same for width, so safeArea is entirely useless right now, since it only conveys information you can get from Screen.height and Screen.width), you can't actually detect where the notch is... this means in this workaround I assume that the notch is at the top of the screen (a safe assumption, but some phones have two notches, one on top and one on bottom, and for those devices my workaround will leave the ad partially covered by the bottom notch... if safeArea ever gets useful values, my workaround can account for that... anyway, I digress.)...

With that assumption made we can use a slightly modified formula: int yDP = DensityIndependentPixels(Screen.height - ScreenPixels(adSize.Height));

But frustratingly while I was logging values I'd expect for the y position... it was still showing up in the same weird offset position! I started manually increasing the y position from 0 incrementally and confirmed that at a certain y coordinate, even if you increase the value, the position it would appear in would be the same! And then I discovered that if you add a fair bit more to the y value, it would inexplicably pop back up at the top of the screen and you could continue incrementing the y position to bring it back down the screen, except this time it didn't get stuck at the weird offset position! After much experimentation I found that the formula for this inexplicable extra wrap-around y offset is: (DensityIndependentPixels(Display.main.systemHeight) + adSize.Height) and when added to intendedDensityIndependentPixelPosition... you now have the banner in the y position you were expecting!

Once you simplify the math, the manual y position that is a functional workaround for placing the banner at the bottom on a notched device is the shockingly simple:

int yDP = DensityIndependentPixels(Screen.height + Display.main.systemHeight);

I've tested this with all the simulated notches in Android 9 and it works (with the caveat that the double notch partially covers the ad still, but that's better than the ad covering the UI of the app), but be warned that I haven't yet tested this on actual notched devices!

Note that you only need to do this when Screen.height != Display.main.systemHeight... otherwise you're on a notchless device and should use AdPosition.BOTTOM.

For those who want your banner to appear at the top, the manual computation for y position is even simpler. Under the same assumption that notches are always on top, the y position of the ad should be set to DensityIndependentPixels(Display.main.systemHeight - Screen.height) with no bizarre inexplicable wrap-around offset necessary. Note that I haven't tested displaying the banner at the top given that the banner in my app is always displayed at the bottom.

The x position manual formula to make sure the ad is centered is exactly as you'd expect: int xDP = Mathf.RoundToInt(DensityIndependentPixels(Display.main.systemWidth - ScreenPixels(adSize.Width)) / 2f); Nothing particularly weird there.

One final note, I noticed that after I started manually specifying the ad position, I couldn't call Show() after calling Hide() on the BannerView... it would get in a weird state where it was invisible yet clickable; to fix this simply Destroy() and create a new AdRequest instead of calling Show()... I've seen some statements on Google support forums that say this is better practice anyway.

I hope the day I lost diving into this bizarre issue and finding this workaround helps someone here.

Unfortunately this is an issue that is caused by admob not accounting correctly for the top notch on Android. Note on iOS the ads code seems to acknowledge the existence of the notch and add extra 10 units of padding under it, which is weird.

I just spent a day or so trying to figure out very similar issue with the banner.

There are several ways to fix this:

Option 1. Create new java plugin and add the code below or from C# call the equivalent of this code:

public int getDisplayRealHeightPixels() {
    Point realSize = new Point();
    Display display = getWindowManager().getDefaultDisplay();
    if (Build.VERSION.SDK_INT >= 17) {
        display.getRealSize(realSize);
    }
    else {
        display.getSize(realSize);
    }
    return realSize.y;
}

Then use getDisplayRealHeightPixels() to position the banner. That's what I currently do.

Option 2. Enable painting in cutout area: create a new java plugin, in onCreate add:

protected void onCreate(Bundle savedInstanceState) {
    ...
    // uncomment to allow painting in notch area;
    WindowManager.LayoutParams attributes = getWindow().getAttributes();
    attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
    getWindow().setAttributes(attributes);
    ...

I'll use this option once I adapt my app to support cutouts properly.

Option 3: Wait for Unity 2018.3 to support notches properly

According to https://unity3d.com/unity/beta/2018.3:

"Android: Added notch support for Android."


Gotchas:

  1. None of the above fixes are perfect (perhaps option 3? but I haven't tried it.) Make sure to test on iOS (iPhone X) because Ads behaves differently there than Android.
  2. On Android Ads, y==0 is the top of the screen, while on iPhone X y==0 renders the banner about 10 units below the notch. For iOS I just hardcode the "notch size" to 30 and draw my app in the notch area.
  3. Android split screen is a pain - I haven't tested completely there, my banner flies around the screen in this case. I just decided to fix it later.
Related