v3.2.5: more Android display cutout stuff:

- added proper support for top-left cutouts, hero pane can extend a bit
- made Android 'smaller cutout' detection more permissive again
- added a check to not trust devices with no reported cutout
This commit is contained in:
Evan Debenham
2025-09-17 17:02:54 -04:00
parent 6daaa1febf
commit e4a334412d
3 changed files with 56 additions and 16 deletions

View File

@@ -117,17 +117,29 @@ public class AndroidPlatformSupport extends PlatformSupport {
if (cutout != null) {
boolean largeCutout = false;
boolean cutoutsPresent = false;
int screenSize = Game.width * Game.height;
for (Rect r : cutout.getBoundingRects()){
for (Rect r : cutout.getBoundingRects()) {
//use abs as some cutouts can apparently be returned inverted
int cutoutSize = Math.abs(r.height() * r.width());
//display cutouts are considered large if they take up more than 0.667% of the screen
//in reality we want less than about 0.5%, but some cutouts over-report their size
//Pixel devices especially =S
if (cutoutSize*150 >= screenSize){
largeCutout = true;
//display cutouts are considered large if they take up more than 0.75%
// of the screen/ in reality we want less than about 0.5%,
// but some cutouts over-report their size, Pixel devices especially =S
if (cutoutSize > 0){
cutoutsPresent = true;
if (cutoutSize * 133.33f >= screenSize) {
largeCutout = true;
}
}
}
if (!cutoutsPresent){
//if we get no cutouts reported, assume the device is lying to us
// and there actually is a cutout, which we must assume is large =S
largeCutout = true;
}
if (largeCutout || level == INSET_ALL) {
insets.left = Math.max(insets.left, cutout.getSafeInsetLeft());
insets.top = Math.max(insets.top, cutout.getSafeInsetTop());

View File

@@ -369,10 +369,11 @@ public class GameScene extends PixelScene {
int uiSize = SPDSettings.interfaceSize();
//display cutouts can obstruct various UI elements, so we need to adjust for that sometimes
float heroPaneExtraWidth = insets.left;
float menuBarMaxLeft = uiCamera.width-insets.right-MenuPane.WIDTH;
int hpBarMaxWidth = 50; //default max width
float buffBarTopRowMaxWidth = 50; //default max width
if (largeInsetTop != insets.top){
if (largeInsetTop == 0){
//iOS's Dynamic island badly obstructs the first buff bar row
if (DeviceCompat.isiOS()){
//TODO bad to hardcode and approximate this atm
@@ -380,10 +381,18 @@ public class GameScene extends PixelScene {
float cutoutLeft = (Game.width*0.3f)/defaultZoom;
buffBarTopRowMaxWidth = Math.min(50, cutoutLeft - 32);
} else if (DeviceCompat.isAndroid()) {
//Android hole punches are of varying size and may obstruct the menu, HP bar, or buff bar
//Android hole punches are of varying size and may obstruct various UI elements
RectF cutout = Game.platform.getDisplayCutout().scale(1f / defaultZoom);
//if the cutout is positioned to obstruct the hero portrait in the status pane
if (cutout.top < 30
&& cutout.left < 20
&& cutout.right > 12) {
heroPaneExtraWidth = Math.max(heroPaneExtraWidth, cutout.right-12);
//make sure we have space to actually move it though
heroPaneExtraWidth = Math.min(heroPaneExtraWidth, uiCamera.width - PixelScene.MIN_WIDTH_P);
}
//if the cutout is positioned to obstruct the menu bar
if (cutout.top < 20
else if (cutout.top < 20
&& cutout.left < menuBarMaxLeft + MenuPane.WIDTH
&& cutout.right > menuBarMaxLeft) {
menuBarMaxLeft = Math.min(menuBarMaxLeft, cutout.left - MenuPane.WIDTH);
@@ -391,7 +400,7 @@ public class GameScene extends PixelScene {
menuBarMaxLeft = Math.max(menuBarMaxLeft, PixelScene.MIN_WIDTH_P-MenuPane.WIDTH);
}
//if the cutout is positioned to obstruct the HP bar
if (cutout.left < 78
else if (cutout.left < 78
&& cutout.top < 4
&& cutout.right > 32) {
//subtract starting position, but add a bit back due to end of bar
@@ -432,6 +441,7 @@ public class GameScene extends PixelScene {
status = new StatusPane( SPDSettings.interfaceSize() > 0 );
status.camera = uiCamera;
StatusPane.heroPaneExtraWidth = heroPaneExtraWidth;
StatusPane.hpBarMaxWidth = hpBarMaxWidth;
StatusPane.buffBarTopRowMaxWidth = buffBarTopRowMaxWidth;
status.setRect(insets.left, uiSize > 0 ? uiCamera.height-39-insets.bottom : screentop, uiCamera.width - insets.left - insets.right, 0 );

View File

@@ -78,6 +78,9 @@ public class StatusPane extends Component {
private boolean large;
//potentially extends the hero portrait space to avoid some cutouts
public static float heroPaneExtraWidth = 0;
private NinePatch heroPaneCutout;
//potentially shrinks and/or repositions the hp bar to avoid some cutouts
public static int hpBarMaxWidth = 50;
private Image hpCutout;
@@ -95,6 +98,10 @@ public class StatusPane extends Component {
else bg = new NinePatch( asset, 0, 0, 82, 38, 32, 0, 5, 0 );
add( bg );
heroPaneCutout = new NinePatch(asset, 0, 0, 5, 36, 4, 0, 0, 0);
heroPaneCutout.visible = false;
add(heroPaneCutout);
hpCutout = new Image(asset, 90, 0, 12, 9);
hpCutout.visible = false;
add(hpCutout);
@@ -181,7 +188,9 @@ public class StatusPane extends Component {
height = large ? 39 : 38;
bg.x = x;
float heroPaneWidth = 30 + heroPaneExtraWidth;
bg.x = x + heroPaneExtraWidth;
bg.y = y;
if (large) bg.size( 160, bg.height ); //HP bars must be 128px wide atm
else bg.size(hpBarMaxWidth+32, bg.height ); //default max right is 50px health bar + 32
@@ -190,7 +199,7 @@ public class StatusPane extends Component {
avatar.y = bg.y - avatar.height / 2f + 16;
PixelScene.align(avatar);
heroInfo.setRect( x, y, 30, large ? 40 : 36 );
heroInfo.setRect( x, y, heroPaneWidth, large ? 40 : 36 );
compass.x = avatar.x + avatar.width / 2f - compass.origin.x;
compass.y = avatar.y + avatar.height / 2f - compass.origin.y;
@@ -221,7 +230,14 @@ public class StatusPane extends Component {
exp.x = x+2;
exp.y = y+30;
float hpleft = x + 30;
if (heroPaneExtraWidth > 0){
heroPaneCutout.visible = true;
heroPaneCutout.x = x;
heroPaneCutout.y = y;
heroPaneCutout.size(heroPaneExtraWidth+4, heroPaneCutout.height);
}
float hpleft = x + heroPaneWidth;
if (hpBarMaxWidth < 82){
//the class variable assumes the left of the bar can't move, but we can inset it 9px
int hpWidth = (int)hpBarMaxWidth;
@@ -255,7 +271,7 @@ public class StatusPane extends Component {
heroInfoOnBar.setRect(heroInfo.right(), y, 50, 9);
buffs.firstRowWidth = buffBarTopRowMaxWidth;
buffs.setRect( x + 31, y + 8, 50, 15 );
buffs.setRect( x + heroPaneWidth + 1, y + 8, 50, 15 );
busy.x = x + 1;
busy.y = y + 37;
@@ -322,7 +338,7 @@ public class StatusPane extends Component {
expText.x = hp.x + (128 - expText.width())/2f;
} else {
exp.scale.x = (17 / exp.width) * Dungeon.hero.exp / Dungeon.hero.maxExp();
exp.scale.x = ((17 + heroPaneExtraWidth) / exp.width) * Dungeon.hero.exp / Dungeon.hero.maxExp();
expText.text(Dungeon.hero.exp + "/" + Dungeon.hero.maxExp());
}
@@ -342,7 +358,7 @@ public class StatusPane extends Component {
} else {
level.text( Integer.toString( lastLvl ) );
level.measure();
level.x = x + 25.5f - level.width() / 2f;
level.x = x + heroPaneExtraWidth + 25.5f - level.width() / 2f;
level.y = y + 31.0f - level.baseLine() / 2f;
}
PixelScene.align(level);
@@ -364,6 +380,8 @@ public class StatusPane extends Component {
public void alpha( float value ){
value = GameMath.gate(0, value, 1f);
bg.alpha(value);
heroPaneCutout.alpha(value);
hpCutout.alpha(value);
avatar.alpha(value);
rawShielding.alpha(0.5f*value);
shieldedHP.alpha(value);