Android Anyone with tips/pitfalls for dealing with mobile device "notches"?

G

Guest

Guest
I don't have a phone with a notch, hole punch, etc. Does anyone who has dealt with this have any tips or pitfalls specific to GMS2? Even just the basics---do GML's gui/display functions return the resolution you see advertised for the phone, do attempts to draw over it "work" as in not crash, is there any way to detect that the phone has a dead area like that?
 
G

Guest

Guest
I've been looking at this more. Android Virtual Devices running Pie can simulate a tall single notch, a corner hole-punch, and top-and-bottom notches. When these are enabled, GMS2's get_display functions simply return a smaller display height and the game simply acts as if the display were the size of the screen less the vertical space of the notches. This results in a black-bar effect, except you can't overwrite it using the GUI functions the way that you can with aspect-ratio black bars.

According to this article, the Android API provides a function to use the full screen space excepting only the notch area itself. GMS2 doesn't have functionality to do this, though. I'll submit a feature request and I'm sure YYG will update GMS2 promptly to keep up with Android and modern devices.

Edit: The last sentence was sarcasm, for any prospective users of the mobile export.
 
You can find extensions to allow your to change the shape of your game window (for instance, if you wanted a circular window rather than a rectangular one). Perhaps look at some of those.
 
I don't have a phone with a notch, hole punch, etc. Does anyone who has dealt with this have any tips or pitfalls specific to GMS2? Even just the basics---do GML's gui/display functions return the resolution you see advertised for the phone, do attempts to draw over it "work" as in not crash, is there any way to detect that the phone has a dead area like that?
You can use this extension: https://marketplace.yoyogames.com/assets/9034/notch-mobile
 
the user interface/display of the GML solution is very difficult to set up well.
It also depends on whether you want to optimize sprites for different resolutions. 720p 1080p 1440p 2160p

this code snippet is from my current game and obtimates the resolution for each device and also allows to adjust without probleme

global.d_shift = for different sprite sizes for different resolutions ( no need for SWF sprites)
global.display_resolution = "same"

global.w_h_scal = for scaling

Portrait Orientaion
GML:
var _gh = global.height/1280
var _gw = global.width/720


if (_gh <= _gw){
    global.vor_dr = _gh;
}
else{
    global.vor_dr = _gw;
}
  
    global.size_w = 1080;
    global.size_h = 1920;
    global.display_resolution = 1080;
    global.d_shift = 1.5;

    //1280 x 720p 1x
    if (global.vor_dr < 1.5-0.1){
        global.size_w = 720;
        global.size_h = 1280;
        global.display_resolution = 720;
        global.d_shift = 1.0;
    }
    //1920 x 1080p 1.5x
    if (global.vor_dr < 2.0-0.1) and (global.vor_dr >= 1.5-0.1){
        global.size_w = 1080;
        global.size_h = 1920;
        global.display_resolution = 1080;
        global.d_shift = 1.5;
    } 
    //2560 x 1440p 2x
    if (global.vor_dr < 3.0-0.1) and (global.vor_dr > 2.0-0.1){
        global.size_w = 1440;
        global.size_h = 2560;
        global.display_resolution = 1440;
        global.d_shift = 2.0;
    } 
    //3840 x 2160p 3x
    if (global.vor_dr >= 3.0-0.1){
        global.size_w = 2160;
        global.size_h = 3840;
        global.d_shift = 3.0;
        global.display_resolution = 2160;
    }
var _gh = global.height/global.size_h
var _gw = global.width/global.size_w

if (_gh <= _gw){
    global.w_h_scal = _gh;
}
else{
    global.w_h_scal = _gw;
}
 

Fiersking

Member
@Mehdi you re welcome!

if someone need the fullscreen plugin for android phone with a notche here he is :

just import it in your project : tools/import local packge

and run you game on your phone.
physical test phone :
galaxy s22 ultra with notch
xiomii with notch

samsung s8+ (no notch)
logicom old phone (no notch)

if you need to tweak the java code do it ad you want the only thing i ask si to share it after with us :)

tell me if it works in your project


Ps: i'am not gonna give any official support because 1/ i don't have time :) 2/ the plugin is pretty straightforward.
if you have question you can send me a PM but it will take some time before i answer it.

edit : now we have the github ;)
 
Last edited:

Fiersking

Member
My ISP blocks 1fichier.com so I can't grab it.
Give me your mail in Mp i will send it to you if you want

why on some strange file hosting, instead of github, where everyone can fork it (as you said - you don't have time to support it), and fix/extend it like they want?
hi @gnysek Basically I'm not really a developer and I don't necessarily have the right reflexes such as git hub in fact I just wanted to make it accessible to those who wanted it immediately and the first thing I thought of was a file host :)
i'am gonna post it on git hub and post it there after :)

Edit :
here we Go

Thanks @gnysek for the idea ;)
 
Last edited:

chirpy

Member
Thanks for sharing. I tried the code but somehow couldn't get a consistent result.
If I add the code/call to extension Init or onStart it works, but I can't get it to work by calling it as an extension function once the game starts (using init script or any event).
It also only works on "Full scale" and not "Keep aspect ratio" Android - Graphics settings. [edit]

I'm not very familar with Android layouts; perhaps we just cannot change the layout programmatically after onCreate / onStart?
(I can set and get layoutInDisplayCutoutMode fine, but the display did not change. Even if I run the code in UI thread.)
Or because YYG runner does not update after layout changes.

Or is it something related to simulated cutouts?
I'm using developer options to simulate cutouts on a physical device that does not have real cutouts.
https://developer.android.com/develop/ui/views/layout/display-cutout
 
Last edited:
simple ways are:
- don't care at all
Seriously: this 👆
Anyways, if it was me, one guy and/or a small team, this is the very last time I'd spend my time working on.
Plus, you virtually can't support every single kind of device variation out there (and ain't expected to)
What you COULD do, which would maybe be the best compromise imo, is just release it for a while, and, after checking analytics, maybe push an update for your couple most popular devices to improve the UI a bit with the notches.
 

chirpy

Member
Seriously: this 👆
Anyways, if it was me, one guy and/or a small team, this is the very last time I'd spend my time working on.
Plus, you virtually can't support every single kind of device variation out there (and ain't expected to)
What you COULD do, which would maybe be the best compromise imo, is just release it for a while, and, after checking analytics, maybe push an update for your couple most popular devices to improve the UI a bit with the notches.
It is a very good point. Once you enabled the fullscreen / cutout mode, you have to worry about for example buttons/ads/banners being blocked by the cutout area, and hence getting into handling DisplayCutout insets and converting between pixels and dp etc. Things to hate on iOS devices. Having the choice to leave the display with black areas by default is a gift from Google and Android devices.
 
It is a very good point. Once you enabled the fullscreen / cutout mode, you have to worry about for example buttons/ads/banners being blocked by the cutout area, and hence getting into handling DisplayCutout insets and converting between pixels and dp etc. Things to hate on iOS devices. Having the choice to leave the display with black areas by default is a gift from Google and Android devices.
And having the 'x' button on the ad blocked by a notch would probably be uber-frustrating, especially if rn-jesus decides to try to push you a 2 minutes long ad! 😅
 

chirpy

Member
Oh well, I have to some of take my words back since latest AdMob seems to render with default or never cutout mode so it's one less thing to worry about.

Still, Android has many variations of cutouts, making it difficult to maintain and dynamically apply UI paddings when device orientation changes.

All said, it wasn't all too bad, since Android API returns actual pixel insets instead of dp like iOS does iirc.
For what it's worth, here's a function I added to get Android cutout insets, following this iOS extension's return json format.

Java:
import android.os.Build;
import android.view.DisplayCutout;

public String get_cutout_safe_area()
{
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
    {
        DisplayCutout displayCutout = RunnerActivity.CurrentActivity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout();
        if (displayCutout == null)
        {
            return "{\"detected\":0,\"top\":0,\"bottom\":0,\"left\":0,\"right\":0}";
        }
        else
        {
            int topPadding = displayCutout.getSafeInsetTop();
            int bottomPadding = displayCutout.getSafeInsetBottom();
            int leftPadding = displayCutout.getSafeInsetLeft();
            int rightPadding = displayCutout.getSafeInsetRight();
            return String.format("{\"detected\":1,\"top\":%d,\"bottom\":%d,\"left\":%d,\"right\":%d}", topPadding, bottomPadding, leftPadding, rightPadding);
        }
    }
    else
    {
        return "{\"detected\":0,\"top\":0,\"bottom\":0,\"left\":0,\"right\":0}";
    }
}

// to notify the game about rotation changes
int _screenRotation = -1;
public void onWindowFocusChanged(boolean hasFocus)
{
    if (!hasFocus) return;

    int newRotation = RunnerActivity.CurrentActivity.getWindowManager().getDefaultDisplay().getRotation();
    if (newRotation != _screenRotation)
    {
        _screenRotation = newRotation;

        int dsMapIndex = RunnerJNILib.jCreateDsMap(null, null, null);
        RunnerJNILib.DsMapAddString( dsMapIndex, "type", "orientation_changed" );
        RunnerJNILib.CreateAsynEventWithDSMap(dsMapIndex, EVENT_OTHER_SOCIAL);
    }
}
 
Last edited:

Fiersking

Member
@chirpy
when i try your solution i have this in log :
Exception thrown attempting to create extension class null
because of that :
DisplayCutout displayCutout = RunnerActivity.CurrentActivity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout();


edit : sorry it was my bad because i lauch the cutout chech at startup (maybe?)
 
Last edited:

chirpy

Member
@chirpy
when i try your solution i have this in log :
Exception thrown attempting to create extension class null
because of that :
DisplayCutout displayCutout = RunnerActivity.CurrentActivity.getWindow().getDecorView().getRootWindowInsets().getDisplayCutout();
I'm not sure how you used the code snippets; they are functions and not an extension, so you probably need to paste them into your extension.
The extension class will need to extend ExtensionBase for the onWindowFocusChanged callback method to work. (reference: Android extension callbacks)
Java:
public class FullScreenMode extends ExtensionBase
{
// ... methods
}
With that said, I later found out that this thread mentioned a seemingly perfectly ready $5 extension for all we had done.
 

Fiersking

Member
@chirpy sorry i forgot to valid my answer but you are right : i add this outside the function but know it's ok i'am gonna make an update of the extention i made with your lights thanks :)
 

Fiersking

Member
@chirpy i have a question for you
with the extention the purpose was to find the exact location of the cutout edge (the notche?) to now were it was in order to design our UI around.

with your code i had 0 every were (except top:88)so i change this part of the code :
Java:
int topPadding = displayCutout.getSafeInsetTop();
            int bottomPadding = displayCutout.getSafeInsetBottom();
            int leftPadding = displayCutout.getSafeInsetLeft();
            int rightPadding = displayCutout.getSafeInsetRight();
            return String.format("{\"detected\":1,\"top\":%d,\"bottom\":%d,\"left\":%d,\"right\":%d}", topPadding, bottomPadding, leftPadding, rightPadding);

for this :

Code:
                List<Rect> RCT= displayCutout.getBoundingRects();
                int RCT_Size=RCT.size();
                for(int i=0;i<RCT_Size;i++){
                    Rect res=RCT.get(i);
                    leftPadding=res.left;
                    rightPadding=res.right;
                    topPadding=res.top;
                    bottomPadding=res.bottom;
                    
                    
                }
                
                
                return String.format("{\"detected\":Yes,\"top\":%d,\"bottom\":%d,\"left\":%d,\"right\":%d}", topPadding, bottomPadding, leftPadding, rightPadding);
with your code i found 0 every were except on top : 88 (the bottom of the camera)

with the second code i find were the camera start left were it ends on the right were it start on top and were it ends on the bottom.


so my question : what is the difference between displayCutout.getBoundingRects() and displayCutout.getSafeInsetLeft() ?

i try to read the android doc but didn't understood anything.

Maybe it's something like apple : displayCutout.getSafeInsetLeft() give you info if the edge is not available or used for something like the bottom of the iphone X ?
 
Top