diff options
author | Ivan Solovev <ivan.solovev@qt.io> | 2022-02-23 18:26:09 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2022-03-04 16:32:47 +0000 |
commit | 51c8007dc71eae4fac35a7d6bf4488b09cee4b52 (patch) | |
tree | 51fa7c0c31ec30d3a00287d9300862b7c6920b7a /src | |
parent | 179d540574af56bcb7a7047aec6a796cae22914c (diff) |
Android: make sure that orientationChange is reported after resize
On Android orientation changes are reported from the
DisplayListener.onDisplayChanged() method, while the screen size
changes are reported from QtLayout.onSizeChanged().
In practice these callbacks come in random order, so rotating the
screen multiple times might result in inconsistent order of signals
and events.
This patch makes sure that size change events always happen before
orientation changes. This is done by caching the new orientation
values and reporting them only when needed. At this point we also
need to use QMetaObject::invokeMethod() for orientation change,
like it is done for geometry change. Otherwise the orientation
update can still be processed earlier than the geometry change.
Also note that at some point we might get an orientation change
without a size change (for example Qt::LandscapeOrientation ->
Qt::InvertedLandscapeOrientation). That is the reason for
isSimilarRotation() helper function.
As a drive-by: ignore size changes with inconsistent values when window
size is reported to have old orientation, while the screen has already
been rotated. In such cases a new size change will be triggered shortly
with normal value.
Task-number: QTBUG-94459
Change-Id: I5c98e526d0370d380344b2297169d5e0c0ee8ea7
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
(cherry picked from commit 79fb80152ff29a30faa3f0cce79a602e36cadbeb)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
Diffstat (limited to 'src')
5 files changed, 77 insertions, 7 deletions
diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java index 9a43f85e4d..c6ae62273e 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2017 BogDan Vatra <bogdan@kde.org> -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Copyright (C) 2016 Olivier Goffart <ogoffart@woboq.com> ** Contact: https://www.qt.io/licensing/ ** @@ -693,13 +693,27 @@ public class QtActivityDelegate @Override public void onDisplayAdded(int displayId) { } + private boolean isSimilarRotation(int r1, int r2) + { + return (r1 == r2) + || (r1 == Surface.ROTATION_0 && r2 == Surface.ROTATION_180) + || (r1 == Surface.ROTATION_180 && r2 == Surface.ROTATION_0) + || (r1 == Surface.ROTATION_90 && r2 == Surface.ROTATION_270) + || (r1 == Surface.ROTATION_270 && r2 == Surface.ROTATION_90); + } + @Override public void onDisplayChanged(int displayId) { Display display = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) ? m_activity.getWindowManager().getDefaultDisplay() : m_activity.getDisplay(); m_currentRotation = display.getRotation(); - QtNative.handleOrientationChanged(m_currentRotation, m_nativeOrientation); + m_layout.setActivityDisplayRotation(m_currentRotation); + // Process orientation change only if it comes after the size + // change, or if the screen is rotated by 180 degrees. + // Otherwise it will be processed in QtLayout. + if (isSimilarRotation(m_currentRotation, m_layout.displayRotation())) + QtNative.handleOrientationChanged(m_currentRotation, m_nativeOrientation); float refreshRate = display.getRefreshRate(); QtNative.handleRefreshRateChanged(refreshRate); } @@ -831,6 +845,7 @@ public class QtActivityDelegate else m_nativeOrientation = Configuration.ORIENTATION_PORTRAIT; + m_layout.setNativeOrientation(m_nativeOrientation); QtNative.handleOrientationChanged(rotation, m_nativeOrientation); m_currentRotation = rotation; diff --git a/src/android/jar/src/org/qtproject/qt/android/QtLayout.java b/src/android/jar/src/org/qtproject/qt/android/QtLayout.java index 2e52770cc5..7cb694dacf 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtLayout.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtLayout.java @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Copyright (C) 2012 BogDan Vatra <bogdan@kde.org> ** Contact: https://www.qt.io/licensing/ ** @@ -53,6 +53,26 @@ import android.view.WindowInsets; public class QtLayout extends ViewGroup { private Runnable m_startApplicationRunnable; + + private int m_activityDisplayRotation = -1; + private int m_ownDisplayRotation = -1; + private int m_nativeOrientation = -1; + + public void setActivityDisplayRotation(int rotation) + { + m_activityDisplayRotation = rotation; + } + + public void setNativeOrientation(int orientation) + { + m_nativeOrientation = orientation; + } + + public int displayRotation() + { + return m_ownDisplayRotation; + } + public QtLayout(Context context, Runnable startRunnable) { super(context); @@ -80,6 +100,17 @@ public class QtLayout extends ViewGroup : ((Activity)getContext()).getDisplay(); display.getRealMetrics(realMetrics); + if ((realMetrics.widthPixels > realMetrics.heightPixels) != (w > h)) { + // This is an intermediate state during display rotation. + // The new size is still reported for old orientation, while + // realMetrics contain sizes for new orientation. Setting + // such parameters will produce inconsistent results, so + // we just skip them. + // We will have another onSizeChanged() with normal values + // a bit later. + return; + } + boolean isFullScreenView = h == realMetrics.heightPixels; int insetLeft = isFullScreenView ? insets.getSystemWindowInsetLeft() : 0; @@ -95,6 +126,17 @@ public class QtLayout extends ViewGroup usableAreaWidth, usableAreaHeight, realMetrics.xdpi, realMetrics.ydpi, realMetrics.scaledDensity, realMetrics.density, display.getRefreshRate()); + int newRotation = display.getRotation(); + if (m_ownDisplayRotation != m_activityDisplayRotation + && newRotation == m_activityDisplayRotation) { + // If the saved rotation value does not match the one from the + // activity, it means that we got orientation change before size + // change, and the value was cached. So we need to notify about + // orientation change now. + QtNative.handleOrientationChanged(newRotation, m_nativeOrientation); + } + m_ownDisplayRotation = newRotation; + if (m_startApplicationRunnable != null) { m_startApplicationRunnable.run(); m_startApplicationRunnable = null; diff --git a/src/plugins/platforms/android/androidjnimain.cpp b/src/plugins/platforms/android/androidjnimain.cpp index 280cfa2c53..e584e7308f 100644 --- a/src/plugins/platforms/android/androidjnimain.cpp +++ b/src/plugins/platforms/android/androidjnimain.cpp @@ -1,7 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2014 BogDan Vatra <bogdan@kde.org> -** Copyright (C) 2021 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the plugins of the Qt Toolkit. @@ -541,6 +541,9 @@ static void startQtApplication(JNIEnv */*env*/, jclass /*clazz*/) vm->AttachCurrentThread(&env, &args); } + // Register type for invokeMethod() calls. + qRegisterMetaType<Qt::ScreenOrientation>("Qt::ScreenOrientation"); + // Register resources if they are available if (QFile{QStringLiteral("assets:/android_rcc_bundle.rcc")}.exists()) QResource::registerResource(QStringLiteral("assets:/android_rcc_bundle.rcc")); @@ -757,9 +760,13 @@ static void handleOrientationChanged(JNIEnv */*env*/, jobject /*thiz*/, jint new QAndroidPlatformIntegration::setScreenOrientation(screenOrientation, native); QMutexLocker lock(&m_platformMutex); if (m_androidPlatformIntegration) { - QPlatformScreen *screen = m_androidPlatformIntegration->screen(); - QWindowSystemInterface::handleScreenOrientationChange(screen->screen(), - screenOrientation); + QAndroidPlatformScreen *screen = m_androidPlatformIntegration->screen(); + // Use invokeMethod to keep the certain order of the "geometry change" + // and "orientation change" event handling. + if (screen) { + QMetaObject::invokeMethod(screen, "setOrientation", Qt::AutoConnection, + Q_ARG(Qt::ScreenOrientation, screenOrientation)); + } } } diff --git a/src/plugins/platforms/android/qandroidplatformscreen.cpp b/src/plugins/platforms/android/qandroidplatformscreen.cpp index 59c8f396af..0ba3c17f88 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.cpp +++ b/src/plugins/platforms/android/qandroidplatformscreen.cpp @@ -291,6 +291,11 @@ void QAndroidPlatformScreen::setRefreshRate(qreal refreshRate) QWindowSystemInterface::handleScreenRefreshRateChange(QPlatformScreen::screen(), refreshRate); } +void QAndroidPlatformScreen::setOrientation(Qt::ScreenOrientation orientation) +{ + QWindowSystemInterface::handleScreenOrientationChange(QPlatformScreen::screen(), orientation); +} + void QAndroidPlatformScreen::setAvailableGeometry(const QRect &rect) { QMutexLocker lock(&m_surfaceMutex); diff --git a/src/plugins/platforms/android/qandroidplatformscreen.h b/src/plugins/platforms/android/qandroidplatformscreen.h index e0de85277a..0f3d02a183 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.h +++ b/src/plugins/platforms/android/qandroidplatformscreen.h @@ -94,6 +94,7 @@ public slots: void setAvailableGeometry(const QRect &rect); void setSize(const QSize &size); void setRefreshRate(qreal refreshRate); + void setOrientation(Qt::ScreenOrientation orientation); protected: bool event(QEvent *event) override; |