diff --git a/cocos/platform/android/CCGLViewImpl-android.cpp b/cocos/platform/android/CCGLViewImpl-android.cpp index c16927973bb2..519584f383fe 100644 --- a/cocos/platform/android/CCGLViewImpl-android.cpp +++ b/cocos/platform/android/CCGLViewImpl-android.cpp @@ -144,6 +144,8 @@ Rect GLViewImpl::getSafeAreaRect() const { bool isScreenRound = JniHelper::callStaticBooleanMethod("org/cocos2dx/lib/Cocos2dxHelper", "isScreenRound"); bool hasSoftKeys = JniHelper::callStaticBooleanMethod("org/cocos2dx/lib/Cocos2dxHelper", "hasSoftKeys"); + bool isCutoutEnabled = JniHelper::callStaticBooleanMethod("org/cocos2dx/lib/Cocos2dxHelper", "isCutoutEnabled"); + if(isScreenRound) { // edge screen (ex. Samsung Galaxy s7, s9, s9+, Note 9, Nokia 8 Sirocco, Sony Xperia XZ3, Oppo Find X...) if(safeAreaRect.size.width < safeAreaRect.size.height) { @@ -184,6 +186,33 @@ Rect GLViewImpl::getSafeAreaRect() const { } } + if (isCutoutEnabled) { + // screen with enabled cutout area (ex. Google Pixel 3 XL, Huawei P20, Asus ZenFone 5, etc) + static int* safeInsets = JniHelper::callStaticIntArrayMethod("org/cocos2dx/lib/Cocos2dxHelper", "getSafeInsets"); + if (safeInsets != nullptr) { + float safeInsetBottom = safeInsets[0] / _scaleY; + float safeInsetLeft = safeInsets[1] / _scaleX; + float safeInsetRight = safeInsets[2] / _scaleX; + 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; + } + } + } + return safeAreaRect; } diff --git a/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java b/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java index d8fc5ba68bf3..a86c566a2585 100644 --- a/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java +++ b/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java @@ -25,7 +25,9 @@ of this software and associated documentation files (the "Software"), to deal ****************************************************************************/ package org.cocos2dx.lib; +import android.annotation.SuppressLint; import android.content.pm.PackageManager; +import android.graphics.Rect; import android.media.AudioManager; import android.app.Activity; import android.content.ComponentName; @@ -47,9 +49,11 @@ of this software and associated documentation files (the "Software"), to deal import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; +import android.view.DisplayCutout; import android.view.KeyCharacterMap; import android.view.KeyEvent; import android.view.ViewConfiguration; +import android.view.Window; import android.view.WindowManager; import com.android.vending.expansion.zipfile.APKExpansionSupport; @@ -64,6 +68,7 @@ of this software and associated documentation files (the "Software"), to deal import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.LinkedHashSet; +import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; @@ -771,6 +776,48 @@ public static boolean isScreenRound() { return false; } + /** + * Returns whether the window is always allowed to extend into the DisplayCutout areas on the short edges of the screen. + * + * @return true if the window in display cutout mode on the short edges of the screen, false otherwise + */ + @SuppressLint("InlinedApi") + public static boolean isCutoutEnabled() { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + WindowManager.LayoutParams lp = sActivity.getWindow().getAttributes(); + return lp.layoutInDisplayCutoutMode == WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + } + + return false; + } + + /** + * Returns safe insets array. + * + * @return array of int with safe insets values + */ + @SuppressLint("NewApi") + public static int[] getSafeInsets() { + final int[] safeInsets = new int[]{0, 0, 0, 0}; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + Window cocosWindow = sActivity.getWindow(); + DisplayCutout displayCutout = cocosWindow.getDecorView().getRootWindowInsets().getDisplayCutout(); + // Judge whether it is cutouts (aka notch) screen phone by judge cutout equle to null + if (displayCutout != null) { + List rects = displayCutout.getBoundingRects(); + // Judge whether it is cutouts (aka notch) screen phone by judge cutout rects is null or zero size + if (rects != null && rects.size() != 0) { + safeInsets[0] = displayCutout.getSafeInsetBottom(); + safeInsets[1] = displayCutout.getSafeInsetLeft(); + safeInsets[2] = displayCutout.getSafeInsetRight(); + safeInsets[3] = displayCutout.getSafeInsetTop(); + } + } + } + + return safeInsets; + } + /** * Queries about whether any physical keys exist on the * any keyboard attached to the device and returns true diff --git a/cocos/platform/android/jni/JniHelper.h b/cocos/platform/android/jni/JniHelper.h index fbf0135851a6..c4943083d6ff 100644 --- a/cocos/platform/android/jni/JniHelper.h +++ b/cocos/platform/android/jni/JniHelper.h @@ -188,6 +188,37 @@ class CC_DLL JniHelper return nullptr; } + /** + @brief Call of Java static int* method + @return address of JniMethodInfo if there are proper JniMethodInfo; otherwise nullptr. + */ + template + static int* callStaticIntArrayMethod(const std::string& className, + const std::string& methodName, + Ts... xs) { + static int ret[32]; + cocos2d::JniMethodInfo t; + std::string signature = "(" + std::string(getJNISignature(xs...)) + ")[I"; + if (cocos2d::JniHelper::getStaticMethodInfo(t, className.c_str(), methodName.c_str(), signature.c_str())) { + LocalRefMapType localRefs; + jintArray array = (jintArray) t.env->CallStaticObjectMethod(t.classID, t.methodID, convert(localRefs, t, xs)...); + jsize len = t.env->GetArrayLength(array); + if (len <= 32) { + jint* elems = t.env->GetIntArrayElements(array, 0); + if (elems) { + memcpy(ret, elems, sizeof(int) * len); + t.env->ReleaseIntArrayElements(array, elems, 0); + }; + } + t.env->DeleteLocalRef(t.classID); + deleteLocalRefs(t.env, localRefs); + return &ret[0]; + } else { + reportError(className, methodName, signature); + } + return nullptr; + } + /** @brief Call of Java static Vec3 method @return JniMethodInfo of Vec3 type if there are proper JniMethodInfo; otherwise Vec3(0, 0, 0). diff --git a/templates/cpp-template-default/proj.android/app/src/org/cocos2dx/cpp/AppActivity.java b/templates/cpp-template-default/proj.android/app/src/org/cocos2dx/cpp/AppActivity.java index 3164b839ae0a..661fe75fda9b 100644 --- a/templates/cpp-template-default/proj.android/app/src/org/cocos2dx/cpp/AppActivity.java +++ b/templates/cpp-template-default/proj.android/app/src/org/cocos2dx/cpp/AppActivity.java @@ -26,6 +26,9 @@ of this software and associated documentation files (the "Software"), to deal import android.os.Bundle; import org.cocos2dx.lib.Cocos2dxActivity; +import android.os.Build; +import android.view.WindowManager; +import android.view.WindowManager.LayoutParams; public class AppActivity extends Cocos2dxActivity { @@ -41,6 +44,13 @@ protected void onCreate(Bundle savedInstanceState) { // Don't need to finish it again since it's finished in super.onCreate . return; } + // Make sure we're running on Pie or higher to change cutout mode + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + // Enable rendering into the cutout area + WindowManager.LayoutParams lp = getWindow().getAttributes(); + lp.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES; + getWindow().setAttributes(lp); + } // DO OTHER INITIALIZATION BELOW }