Android: implement safe areas margins
Rely on Android APIs (old and new) to calculate safe area margins, which takes into account the cutout regions and the system bars. Task-number: QTBUG-131519 Task-number: QTBUG-98989 Change-Id: Ie69e6c54df30c76e67d9ca96518e13f9898e6312 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
parent
b63e274a8a
commit
c2b94a15c1
@ -6,11 +6,18 @@ package org.qtproject.qt.android;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
|
import android.graphics.Insets;
|
||||||
|
|
||||||
|
import android.view.DisplayCutout;
|
||||||
import android.view.GestureDetector;
|
import android.view.GestureDetector;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.Surface;
|
import android.view.Surface;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.view.WindowInsets;
|
||||||
|
|
||||||
|
import android.os.Build;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
@ -25,6 +32,7 @@ class QtWindow extends QtLayout implements QtSurfaceInterface {
|
|||||||
private final QtInputConnection.QtInputConnectionListener m_inputConnectionListener;
|
private final QtInputConnection.QtInputConnectionListener m_inputConnectionListener;
|
||||||
|
|
||||||
private static native void setSurface(int windowId, Surface surface);
|
private static native void setSurface(int windowId, Surface surface);
|
||||||
|
private static native void safeAreaMarginsChanged(Insets insets);
|
||||||
static native void windowFocusChanged(boolean hasFocus, int id);
|
static native void windowFocusChanged(boolean hasFocus, int id);
|
||||||
static native void updateWindows();
|
static native void updateWindows();
|
||||||
|
|
||||||
@ -67,6 +75,46 @@ class QtWindow extends QtLayout implements QtSurfaceInterface {
|
|||||||
});
|
});
|
||||||
m_gestureDetector.setIsLongpressEnabled(true);
|
m_gestureDetector.setIsLongpressEnabled(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (getContext() instanceof QtActivityBase) {
|
||||||
|
View decorView = ((Activity) context).getWindow().getDecorView();
|
||||||
|
decorView.setOnApplyWindowInsetsListener((view, insets) -> {
|
||||||
|
Insets safeInsets;
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
int types = WindowInsets.Type.displayCutout() | WindowInsets.Type.systemBars();
|
||||||
|
safeInsets = insets.getInsets(types);
|
||||||
|
} else {
|
||||||
|
int left = 0;
|
||||||
|
int top = 0;
|
||||||
|
int right = 0;
|
||||||
|
int bottom = 0;
|
||||||
|
|
||||||
|
int visibility = view.getSystemUiVisibility();
|
||||||
|
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
|
||||||
|
left = insets.getSystemWindowInsetLeft();
|
||||||
|
top = insets.getSystemWindowInsetTop();
|
||||||
|
right = insets.getSystemWindowInsetRight();
|
||||||
|
bottom = insets.getSystemWindowInsetBottom();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Android 9 and 10 emulators don't seem to be able
|
||||||
|
// to handle this, but let's have the logic here anyway
|
||||||
|
DisplayCutout cutout = insets.getDisplayCutout();
|
||||||
|
if (cutout != null) {
|
||||||
|
left = Math.max(left, cutout.getSafeInsetLeft());
|
||||||
|
top = Math.max(top, cutout.getSafeInsetTop());
|
||||||
|
right = Math.max(right, cutout.getSafeInsetRight());
|
||||||
|
bottom = Math.max(bottom, cutout.getSafeInsetBottom());
|
||||||
|
}
|
||||||
|
|
||||||
|
safeInsets = Insets.of(left, top, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
safeAreaMarginsChanged(safeInsets);
|
||||||
|
|
||||||
|
return insets;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@UsedFromNativeCode
|
@UsedFromNativeCode
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include <qguiapplication.h>
|
#include <qguiapplication.h>
|
||||||
#include <qpa/qwindowsysteminterface.h>
|
#include <qpa/qwindowsysteminterface.h>
|
||||||
#include <private/qhighdpiscaling_p.h>
|
#include <private/qhighdpiscaling_p.h>
|
||||||
|
#include <private/qjnihelpers_p.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ Q_DECLARE_JNI_CLASS(QtWindowInterface, "org/qtproject/qt/android/QtWindowInterfa
|
|||||||
Q_DECLARE_JNI_CLASS(QtInputInterface, "org/qtproject/qt/android/QtInputInterface")
|
Q_DECLARE_JNI_CLASS(QtInputInterface, "org/qtproject/qt/android/QtInputInterface")
|
||||||
Q_DECLARE_JNI_CLASS(QtInputConnectionListener,
|
Q_DECLARE_JNI_CLASS(QtInputConnectionListener,
|
||||||
"org/qtproject/qt/android/QtInputConnection$QtInputConnectionListener")
|
"org/qtproject/qt/android/QtInputConnection$QtInputConnectionListener")
|
||||||
|
Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtWindowInterface")
|
||||||
|
|
||||||
QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
|
QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
|
||||||
: QPlatformWindow(window), m_nativeQtWindow(nullptr),
|
: QPlatformWindow(window), m_nativeQtWindow(nullptr),
|
||||||
@ -126,13 +128,12 @@ void QAndroidPlatformWindow::raise()
|
|||||||
|
|
||||||
QMargins QAndroidPlatformWindow::safeAreaMargins() const
|
QMargins QAndroidPlatformWindow::safeAreaMargins() const
|
||||||
{
|
{
|
||||||
if ((m_windowState & Qt::WindowMaximized) && (window()->flags() & Qt::ExpandedClientAreaHint)) {
|
return m_safeAreaMargins;
|
||||||
QRect availableGeometry = platformScreen()->availableGeometry();
|
}
|
||||||
return QMargins(availableGeometry.left(), availableGeometry.top(),
|
|
||||||
availableGeometry.right(), availableGeometry.bottom());
|
void QAndroidPlatformWindow::setSafeAreaMargins(const QMargins safeMargins)
|
||||||
} else {
|
{
|
||||||
return QPlatformWindow::safeAreaMargins();
|
m_safeAreaMargins = safeMargins;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void QAndroidPlatformWindow::setGeometry(const QRect &rect)
|
void QAndroidPlatformWindow::setGeometry(const QRect &rect)
|
||||||
@ -382,6 +383,42 @@ void QAndroidPlatformWindow::windowFocusChanged(JNIEnv *env, jobject object,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QAndroidPlatformWindow::safeAreaMarginsChanged(JNIEnv *env, jobject object, QtJniTypes::Insets insets)
|
||||||
|
{
|
||||||
|
Q_UNUSED(env)
|
||||||
|
Q_UNUSED(object)
|
||||||
|
|
||||||
|
if (!qGuiApp)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto tlw = qGuiApp->topLevelWindows();
|
||||||
|
if (tlw.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QMargins safeMargins;
|
||||||
|
if (insets.isValid()) {
|
||||||
|
safeMargins = QMargins(
|
||||||
|
insets.getField<int>("left"),
|
||||||
|
insets.getField<int>("top"),
|
||||||
|
insets.getField<int>("right"),
|
||||||
|
insets.getField<int>("bottom"));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (QWindow *window : qGuiApp->topLevelWindows()) {
|
||||||
|
if (!window->handle())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto *pWindow = static_cast<QAndroidPlatformWindow *>(window->handle());
|
||||||
|
if (!pWindow)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (safeMargins != pWindow->safeAreaMargins()) {
|
||||||
|
pWindow->setSafeAreaMargins(safeMargins);
|
||||||
|
QWindowSystemInterface::handleSafeAreaMarginsChanged(window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void updateWindows(JNIEnv *env, jobject object)
|
static void updateWindows(JNIEnv *env, jobject object)
|
||||||
{
|
{
|
||||||
Q_UNUSED(env)
|
Q_UNUSED(env)
|
||||||
@ -425,11 +462,12 @@ QMutexLocker<QMutex> QAndroidPlatformWindow::destructionGuard()
|
|||||||
bool QAndroidPlatformWindow::registerNatives(QJniEnvironment &env)
|
bool QAndroidPlatformWindow::registerNatives(QJniEnvironment &env)
|
||||||
{
|
{
|
||||||
if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtWindow>::className(),
|
if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtWindow>::className(),
|
||||||
{
|
{
|
||||||
Q_JNI_NATIVE_METHOD(updateWindows),
|
Q_JNI_NATIVE_METHOD(updateWindows),
|
||||||
Q_JNI_NATIVE_SCOPED_METHOD(setSurface, QAndroidPlatformWindow),
|
Q_JNI_NATIVE_SCOPED_METHOD(setSurface, QAndroidPlatformWindow),
|
||||||
Q_JNI_NATIVE_SCOPED_METHOD(windowFocusChanged, QAndroidPlatformWindow)
|
Q_JNI_NATIVE_SCOPED_METHOD(windowFocusChanged, QAndroidPlatformWindow),
|
||||||
})) {
|
Q_JNI_NATIVE_SCOPED_METHOD(safeAreaMarginsChanged, QAndroidPlatformWindow)
|
||||||
|
})) {
|
||||||
qCCritical(lcQpaWindow) << "RegisterNatives failed for"
|
qCCritical(lcQpaWindow) << "RegisterNatives failed for"
|
||||||
<< QtJniTypes::Traits<QtJniTypes::QtWindow>::className();
|
<< QtJniTypes::Traits<QtJniTypes::QtWindow>::className();
|
||||||
return false;
|
return false;
|
||||||
|
@ -20,6 +20,7 @@ QT_BEGIN_NAMESPACE
|
|||||||
Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow)
|
Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow)
|
||||||
Q_DECLARE_JNI_CLASS(QtWindow, "org/qtproject/qt/android/QtWindow")
|
Q_DECLARE_JNI_CLASS(QtWindow, "org/qtproject/qt/android/QtWindow")
|
||||||
Q_DECLARE_JNI_CLASS(Surface, "android/view/Surface")
|
Q_DECLARE_JNI_CLASS(Surface, "android/view/Surface")
|
||||||
|
Q_DECLARE_JNI_CLASS(Insets, "android/graphics/Insets")
|
||||||
|
|
||||||
class QAndroidPlatformScreen;
|
class QAndroidPlatformScreen;
|
||||||
|
|
||||||
@ -53,6 +54,7 @@ public:
|
|||||||
QAndroidPlatformScreen *platformScreen() const;
|
QAndroidPlatformScreen *platformScreen() const;
|
||||||
|
|
||||||
QMargins safeAreaMargins() const override;
|
QMargins safeAreaMargins() const override;
|
||||||
|
void setSafeAreaMargins(const QMargins safeMargins);
|
||||||
|
|
||||||
void propagateSizeHints() override;
|
void propagateSizeHints() override;
|
||||||
void requestActivateWindow() override;
|
void requestActivateWindow() override;
|
||||||
@ -99,11 +101,15 @@ protected:
|
|||||||
QMutex m_surfaceMutex;
|
QMutex m_surfaceMutex;
|
||||||
QMutex m_destructionMutex;
|
QMutex m_destructionMutex;
|
||||||
|
|
||||||
|
QMargins m_safeAreaMargins;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void setSurface(JNIEnv *env, jobject obj, jint windowId, QtJniTypes::Surface surface);
|
static void setSurface(JNIEnv *env, jobject obj, jint windowId, QtJniTypes::Surface surface);
|
||||||
Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(setSurface)
|
Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(setSurface)
|
||||||
static void windowFocusChanged(JNIEnv *env, jobject object, jboolean focus, jint windowId);
|
static void windowFocusChanged(JNIEnv *env, jobject object, jboolean focus, jint windowId);
|
||||||
Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(windowFocusChanged)
|
Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(windowFocusChanged)
|
||||||
|
static void safeAreaMarginsChanged(JNIEnv *env, jobject obj, QtJniTypes::Insets insets);
|
||||||
|
Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(safeAreaMarginsChanged)
|
||||||
|
|
||||||
[[nodiscard]] QMutexLocker<QMutex> destructionGuard();
|
[[nodiscard]] QMutexLocker<QMutex> destructionGuard();
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user