Skip to content

Commit

Permalink
[Android] Calculate safe area inset values for devices with rounded c…
Browse files Browse the repository at this point in the history
…orners (axmolengine#1934)

* [Android] Add support for factoring in device rounded corner inset values for Android API 31 or higher

* Check for null pointer return values from Java.
  • Loading branch information
rh101 authored May 28, 2024
1 parent d826793 commit b54775d
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 32 deletions.
95 changes: 63 additions & 32 deletions core/platform/android/GLViewImpl-android.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,14 @@ Rect GLViewImpl::getSafeAreaRect() const
bool hasSoftKeys = JniHelper::callStaticBooleanMethod("org/axmol/lib/AxmolEngine", "hasSoftKeys");
bool isCutoutEnabled = JniHelper::callStaticBooleanMethod("org/axmol/lib/AxmolEngine", "isCutoutEnabled");

float insetTop = 0.0f;
float insetBottom = 0.0f;
float insetLeft = 0.0f;
float insetRight = 0.0f;

static int* cornerRadii =
JniHelper::callStaticIntArrayMethod("org/axmol/lib/AxmolEngine", "getDeviceCornerRadii");

if (isScreenRound)
{
// edge screen (ex. Samsung Galaxy s7, s9, s9+, Note 9, Nokia 8 Sirocco, Sony Xperia XZ3, Oppo Find X...)
Expand All @@ -162,28 +170,59 @@ Rect GLViewImpl::getSafeAreaRect() const
// landscape: no changes with X-coords
}
}
else if (deviceAspectRatio >= WIDE_SCREEN_ASPECT_RATIO_ANDROID)
else if (deviceAspectRatio >= WIDE_SCREEN_ASPECT_RATIO_ANDROID || cornerRadii != nullptr)
{
// almost all devices on the market have round corners if
// deviceAspectRatio more than 2 (@see "android.max_aspect" parameter in AndroidManifest.xml)
float bottomMarginIfPortrait = 0;
if (hasSoftKeys)
{
bottomMarginIfPortrait = marginY * 2.f;
}

if (safeAreaRect.size.width < safeAreaRect.size.height)
// cornerRadii is only available in API31+ (Android 12+)
if (cornerRadii != nullptr)
{
// portrait: double margin space if device has soft menu
safeAreaRect.origin.y += bottomMarginIfPortrait;
safeAreaRect.size.height -= (bottomMarginIfPortrait + marginY);
float radiiBottom = cornerRadii[0] / _scaleY;
float radiiLeft = cornerRadii[1] / _scaleX;
float radiiRight = cornerRadii[2] / _scaleX;
float radiiTop = cornerRadii[3] / _scaleY;

if (safeAreaRect.size.width < safeAreaRect.size.height)
{
if (hasSoftKeys)
{
safeAreaRect.origin.y += marginY;
safeAreaRect.size.height -= (marginY * 2);
}

// portrait
insetTop = radiiTop;
insetBottom = radiiBottom;
}
else
{
// landscape
insetLeft = radiiLeft;
insetRight = radiiRight;
}
}
else
{
// landscape: ignore double margin at the bottom in any cases
// prepare signle margin for round corners
safeAreaRect.origin.y += marginY;
safeAreaRect.size.height -= (marginY * 2.f);
float bottomMarginIfPortrait = 0;
if (hasSoftKeys)
{
bottomMarginIfPortrait = marginY * 2.f;
}

if (safeAreaRect.size.width < safeAreaRect.size.height)
{
// portrait: double margin space if device has soft menu
safeAreaRect.origin.y += bottomMarginIfPortrait;
safeAreaRect.size.height -= (bottomMarginIfPortrait + marginY);
}
else
{
// landscape: ignore double margin at the bottom in any cases
// prepare single margin for round corners
safeAreaRect.origin.y += marginY;
safeAreaRect.size.height -= (marginY * 2.f);
}
}
}
else
Expand All @@ -209,24 +248,16 @@ Rect GLViewImpl::getSafeAreaRect() const
float safeInsetTop = safeInsets[3] / _scaleY;

// fit safe area rect with safe insets
if (safeInsetBottom > 0)
{
safeAreaRect.origin.y += safeInsetBottom;
safeAreaRect.size.height -= safeInsetBottom;
}
if (safeInsetLeft > 0)
{
safeAreaRect.origin.x += safeInsetLeft;
safeAreaRect.size.width -= safeInsetLeft;
}
if (safeInsetRight > 0)
{
safeAreaRect.size.width -= safeInsetRight;
}
if (safeInsetTop > 0)
{
safeAreaRect.size.height -= safeInsetTop;
}
auto maxInsetBottom = std::max(safeInsetBottom, insetBottom);
safeAreaRect.origin.y += maxInsetBottom;
safeAreaRect.size.height -= maxInsetBottom;

auto maxInsetLeft = std::max(safeInsetLeft, insetLeft);
safeAreaRect.origin.x += maxInsetLeft;
safeAreaRect.size.width -= maxInsetLeft;

safeAreaRect.size.width -= std::max(safeInsetRight, insetRight);
safeAreaRect.size.height -= std::max(safeInsetTop, insetTop);
}
}

Expand Down
42 changes: 42 additions & 0 deletions core/platform/android/java/src/org/axmol/lib/AxmolEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,10 @@ of this software and associated documentation files (the "Software"), to deal
import android.view.DisplayCutout;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowManager;

import com.android.vending.expansion.zipfile.APKExpansionSupport;
Expand Down Expand Up @@ -636,6 +638,46 @@ public static int[] getSafeInsets() {
return safeInsets;
}

/**
* Returns rounded corner radius array.
*
* @return array of int with rounded corner radius values
*/
@SuppressLint("NewApi")
public static int[] getDeviceCornerRadii() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
final int[] radii = new int[]{0, 0, 0, 0};
Window cocosWindow = sActivity.getWindow();
View view = cocosWindow.getDecorView();
WindowInsets insets = view.getRootWindowInsets();
android.view.RoundedCorner topLeft = insets.getRoundedCorner(android.view.RoundedCorner.POSITION_TOP_LEFT);
android.view.RoundedCorner topRight = insets.getRoundedCorner(android.view.RoundedCorner.POSITION_TOP_RIGHT);
android.view.RoundedCorner bottomLeft = insets.getRoundedCorner(android.view.RoundedCorner.POSITION_BOTTOM_LEFT);
android.view.RoundedCorner bottomRight = insets.getRoundedCorner(android.view.RoundedCorner.POSITION_BOTTOM_RIGHT);
int radiusTopLeft = 0;
int radiusTopRight = 0;
int radiusBottomLeft = 0;
int radiusBottomRight = 0;
if (topLeft != null) radiusTopLeft = topLeft.getRadius();
if (topRight != null) radiusTopRight = topRight.getRadius();
if (bottomLeft != null) radiusBottomLeft = bottomLeft.getRadius();
if (bottomRight != null) radiusBottomRight = bottomRight.getRadius();

int leftRadius = Math.max(radiusTopLeft, radiusBottomLeft);
int topRadius = Math.max(radiusTopLeft, radiusTopRight);
int rightRadius = Math.max(radiusTopRight, radiusBottomRight);
int bottomRadius = Math.max(radiusBottomLeft, radiusBottomRight);

radii[0] = bottomRadius;
radii[1] = leftRadius;
radii[2] = rightRadius;
radii[3] = topRadius;
return radii;
}

return null;
}

/**
* Queries about whether any physical keys exist on the
* any keyboard attached to the device and returns <code>true</code>
Expand Down
24 changes: 24 additions & 0 deletions core/platform/android/jni/JniHelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,14 @@ class AX_DLL JniHelper
LocalRefMapType localRefs;
jfloatArray array =
(jfloatArray)t.env->CallStaticObjectMethod(t.classID, t.methodID, convert(localRefs, t, xs)...);

if (array == nullptr)
{
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
return nullptr;
}

jsize len = t.env->GetArrayLength(array);
if (len <= 32)
{
Expand Down Expand Up @@ -269,6 +277,14 @@ class AX_DLL JniHelper
LocalRefMapType localRefs;
jintArray array =
(jintArray)t.env->CallStaticObjectMethod(t.classID, t.methodID, convert(localRefs, t, xs)...);

if (array == nullptr)
{
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
return nullptr;
}

jsize len = t.env->GetArrayLength(array);
if (len <= 32)
{
Expand Down Expand Up @@ -305,6 +321,14 @@ class AX_DLL JniHelper
LocalRefMapType localRefs;
jfloatArray array =
(jfloatArray)t.env->CallStaticObjectMethod(t.classID, t.methodID, convert(localRefs, t, xs)...);

if (array == nullptr)
{
t.env->DeleteLocalRef(t.classID);
deleteLocalRefs(t.env, localRefs);
return Vec3();
}

jsize len = t.env->GetArrayLength(array);
if (len == 3)
{
Expand Down

0 comments on commit b54775d

Please sign in to comment.