I am having issues with getDisplayCutout()
in my fullscreen Java app.
I can only seem to get the value of the DisplayCutout within the onAttachedToWindow
function. After that function completes, I can never get it again.
Code to get the cutouts:
WindowInsets insets = myActivity.getWindow().getDecorView().getRootWindowInsets();
if (insets != null) {
DisplayCutout displayCutout = insets.getDisplayCutout();
if (displayCutout != null && displayCutout.getBoundingRects().size() > 0) {
// we have cutouts to deal with
}
}
Question
How can I get the display cutouts reliably, from anywhere in the code, at any time once attached to the view hierarchy?
Reproduce Issue
After much investigation I have narrowed it down to a VERY broad problem with fullscreen apps and I am surprised that no-one else is asking about it. We can in fact ignore my app and just work off two template projects which you can make yourselves right now.
In Android studio I'm talking about the Phone and Tablet projects called 'Basic Activity' and 'Fullscreen Activity'. If you create one of each, and make the following changes:
For Basic, change the Manifest to handle configuration changes yourself by adding android:configChanges="orientation|keyboardHidden|screenSize"
under the Activity tag like so:
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="orientation|keyboardHidden|screenSize"
android:theme="@style/AppTheme.NoActionBar">
Now for both of them, add the following two functions to the activity file:
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
WindowInsets insets = getWindow().getDecorView().getRootWindowInsets();
if (insets != null) {
DisplayCutout displayCutout = insets.getDisplayCutout();
if (displayCutout != null && displayCutout.getBoundingRects().size() > 0) {
// we have cutouts to deal with
}
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
WindowInsets insets = getWindow().getDecorView().getRootWindowInsets();
if (insets != null) {
DisplayCutout displayCutout = insets.getDisplayCutout();
if (displayCutout != null && displayCutout.getBoundingRects().size() > 0) {
// we have cutouts to deal with
}
}
}
That's all you need to do to reproduce this issue. I run this on a simulator for Pixel 3 on API Q, on which I have enabled simulating cutouts and have chosen BOTH (so there's a cutout on the bottom and the top)
Now if you breakpoint on the line where we try to get display cutouts (DisplayCutout displayCutout = insets.getDisplayCutout();
), you'll see that on the Basic app, it works on startup and when you change orientation, but in the fullscreen app it only works on startup.
In fact on my application I have tested by using the following code within onAttachedToWindow
:
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
// start a thread so that UI thread execution can continue
WorkerThreadManager.StartWork(new WorkerCallback() {
@Override
public void StartWorkSafe() {
// run the following code on the UI thread
XPlatUtil.RunOnUiThread(new SafeRunnable() {
public synchronized void RunSafe() {
WindowInsets insets = myActivity.getWindow().getDecorView().getRootWindowInsets();
if (insets != null) {
DisplayCutout displayCutout = insets.getDisplayCutout();
if (displayCutout != null && displayCutout.getBoundingRects().size() > 0) {
// we have cutouts to deal with
}
}
}
});
}
});
}
This code starts a thread so that the onAttachedToWindow function can finish running; but the thread immediately sends execution back to the UI thread to check the cutouts.
The delay between the onAttachedToWindow function completing its execution and my code checking for the displayCutout must be in the order of nano seconds, but the cutouts are immediately unavailable.
Any ideas? Is this somehow expected?
Without access to the cutouts when orientation changes I have no recourse but to record the Largest inset (top or bottom in portrait, since long edges cannot have them), and apply it to both the top and bottom in portrait, or the left and right in landscape.
This is because I cannot find a way in android to check WHICH kind of landscape is currently active (eg is the top of the phone on the left, or on the right). If I could check that, I could at least only apply the inset on the edge of the phone where it is needed.