Android: fix and simplify the orientation change logic
Move the orientation change handling to the display manager and call it from the relevant places to repeated and scattered code for the same functionality. Bring back part of 072387edecb2269097821e35f1f232da6c657650 which checks discard a resize event that's not valid that reports not up to date layout size. If DisplayManager.DisplayListener.onDisplay() initiates the orientation change, handle it immediately if we don't expect the size to change, i.e. if the orientation is changing from portrait to inverted portrait and vise versa for landscape. Otherwise, expect the change to be initiated again shortly after from QtLayout.onSizeChanged(). Also, add improve the unit test of the orientation change, and test for the widget size after such changes to make sure QTBUG-94459 is not reached again. Fixes: QTBUG-119430 Task-number: QTBUG-115019 Task-number: QTBUG-94459 Change-Id: I5f060d91531af677ddf891f2af360d5f399e26e5 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Ville Voutilainen <ville.voutilainen@qt.io>
This commit is contained in:
parent
a0bdd2195f
commit
7a9bc220c7
@ -135,7 +135,7 @@ class QtActivityDelegate
|
||||
{
|
||||
m_layout = new QtLayout(m_activity);
|
||||
|
||||
m_displayManager = new QtDisplayManager(m_activity, m_layout);
|
||||
m_displayManager = new QtDisplayManager(m_activity);
|
||||
m_displayManager.registerDisplayListener();
|
||||
|
||||
QtInputDelegate.KeyboardVisibilityListener keyboardVisibilityListener =
|
||||
@ -179,11 +179,7 @@ class QtActivityDelegate
|
||||
m_activity.setContentView(m_layout,
|
||||
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
|
||||
int rotation = m_activity.getWindowManager().getDefaultDisplay().getRotation();
|
||||
int nativeOrientation = QtDisplayManager.getNativeOrientation(m_activity, rotation);
|
||||
m_layout.setNativeOrientation(nativeOrientation);
|
||||
QtDisplayManager.handleOrientationChanged(rotation, nativeOrientation);
|
||||
QtDisplayManager.handleOrientationChanges(m_activity, false);
|
||||
|
||||
handleUiModeChange(m_activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
|
||||
|
||||
|
@ -44,45 +44,30 @@ class QtDisplayManager {
|
||||
public static final int SYSTEM_UI_VISIBILITY_TRANSLUCENT = 2;
|
||||
private int m_systemUiVisibility = SYSTEM_UI_VISIBILITY_NORMAL;
|
||||
|
||||
private static int m_previousRotation = -1;
|
||||
|
||||
private DisplayManager.DisplayListener m_displayListener = null;
|
||||
private final Activity m_activity;
|
||||
|
||||
QtDisplayManager(Activity activity, QtLayout layout)
|
||||
QtDisplayManager(Activity activity)
|
||||
{
|
||||
m_activity = activity;
|
||||
initDisplayListener(layout);
|
||||
initDisplayListener();
|
||||
}
|
||||
|
||||
private void initDisplayListener(QtLayout layout) {
|
||||
private void initDisplayListener() {
|
||||
m_displayListener = new DisplayManager.DisplayListener() {
|
||||
@Override
|
||||
public void onDisplayAdded(int displayId) {
|
||||
QtDisplayManager.handleScreenAdded(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) {
|
||||
handleOrientationChanges(m_activity, false);
|
||||
Display display = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
|
||||
? m_activity.getWindowManager().getDefaultDisplay()
|
||||
: m_activity.getDisplay();
|
||||
int rotation = display != null ? display.getRotation() : Surface.ROTATION_0;
|
||||
layout.setActivityDisplayRotation(rotation);
|
||||
// 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(rotation, layout.displayRotation())) {
|
||||
QtDisplayManager.handleOrientationChanged(rotation,
|
||||
getNativeOrientation(m_activity, rotation));
|
||||
}
|
||||
|
||||
float refreshRate = getRefreshRate(display);
|
||||
QtDisplayManager.handleRefreshRateChanged(refreshRate);
|
||||
QtDisplayManager.handleScreenChanged(displayId);
|
||||
@ -95,6 +80,53 @@ class QtDisplayManager {
|
||||
};
|
||||
}
|
||||
|
||||
private static boolean isSameSizeForOrientations(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);
|
||||
}
|
||||
|
||||
static void handleOrientationChanges(Activity activity, boolean sizeChanged)
|
||||
{
|
||||
int currentRotation = getDisplayRotation(activity);
|
||||
int nativeOrientation = getNativeOrientation(activity, currentRotation);
|
||||
|
||||
if (m_previousRotation == currentRotation)
|
||||
return;
|
||||
|
||||
// If the the current and previous rotations are similar then QtLayout.onSizeChanged()
|
||||
// might not be called, so we can already update the orientation, and rely on this to
|
||||
// called again once the resize event is sent.
|
||||
// Note: Android 10 emulator seems to not always send an event when the orientation
|
||||
// changes, could be a bug in the emulator.
|
||||
boolean noResizeNeeded = isSameSizeForOrientations(m_previousRotation, currentRotation);
|
||||
if (m_previousRotation == -1 || sizeChanged || noResizeNeeded) {
|
||||
QtDisplayManager.handleOrientationChanged(currentRotation, nativeOrientation);
|
||||
m_previousRotation = currentRotation;
|
||||
}
|
||||
}
|
||||
|
||||
private static int getDisplayRotation(Activity activity) {
|
||||
Display display = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ?
|
||||
activity.getWindowManager().getDefaultDisplay() :
|
||||
activity.getDisplay();
|
||||
|
||||
return display != null ? display.getRotation() : 0;
|
||||
}
|
||||
|
||||
private static int getNativeOrientation(Activity activity, int rotation)
|
||||
{
|
||||
int orientation = activity.getResources().getConfiguration().orientation;
|
||||
boolean rot90 = (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
|
||||
boolean isLandscape = (orientation == Configuration.ORIENTATION_LANDSCAPE);
|
||||
if ((isLandscape && !rot90) || (!isLandscape && rot90))
|
||||
return Configuration.ORIENTATION_LANDSCAPE;
|
||||
|
||||
return Configuration.ORIENTATION_PORTRAIT;
|
||||
}
|
||||
|
||||
static float getRefreshRate(Display display)
|
||||
{
|
||||
return display != null ? display.getRefreshRate() : 60.0f;
|
||||
@ -114,21 +146,6 @@ class QtDisplayManager {
|
||||
displayManager.unregisterDisplayListener(m_displayListener);
|
||||
}
|
||||
|
||||
public static int getNativeOrientation(Activity activity, int rotation)
|
||||
{
|
||||
int nativeOrientation;
|
||||
|
||||
int orientation = activity.getResources().getConfiguration().orientation;
|
||||
boolean rot90 = (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270);
|
||||
boolean isLandscape = (orientation == Configuration.ORIENTATION_LANDSCAPE);
|
||||
if ((isLandscape && !rot90) || (!isLandscape && rot90))
|
||||
nativeOrientation = Configuration.ORIENTATION_LANDSCAPE;
|
||||
else
|
||||
nativeOrientation = Configuration.ORIENTATION_PORTRAIT;
|
||||
|
||||
return nativeOrientation;
|
||||
}
|
||||
|
||||
public void setSystemUiVisibility(int systemUiVisibility)
|
||||
{
|
||||
if (m_systemUiVisibility == systemUiVisibility)
|
||||
@ -283,20 +300,4 @@ class QtDisplayManager {
|
||||
width, height, xdpi, ydpi,
|
||||
scaledDensity, density, getRefreshRate(display));
|
||||
}
|
||||
|
||||
public static int getDisplayRotation(Activity activity) {
|
||||
Display display;
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) {
|
||||
final WindowManager windowManager = activity.getWindowManager();
|
||||
display = windowManager.getDefaultDisplay();
|
||||
} else {
|
||||
display = activity.getDisplay();
|
||||
}
|
||||
|
||||
int newRotation = 0;
|
||||
if (display != null) {
|
||||
newRotation = display.getRotation();
|
||||
}
|
||||
return newRotation;
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// Copyright (C) 2023 The Qt Company Ltd.
|
||||
// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
@ -6,31 +6,15 @@ package org.qtproject.qt.android;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
class QtLayout extends ViewGroup
|
||||
{
|
||||
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)
|
||||
{
|
||||
super(context);
|
||||
@ -53,18 +37,28 @@ class QtLayout extends ViewGroup
|
||||
if (activity == null)
|
||||
return;
|
||||
|
||||
QtDisplayManager.setApplicationDisplayMetrics(activity, w, h);
|
||||
DisplayMetrics realMetrics = new DisplayMetrics();
|
||||
Display display = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R)
|
||||
? activity.getWindowManager().getDefaultDisplay()
|
||||
: activity.getDisplay();
|
||||
|
||||
int newRotation = QtDisplayManager.getDisplayRotation(activity);
|
||||
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.
|
||||
QtDisplayManager.handleOrientationChanged(newRotation, m_nativeOrientation);
|
||||
if (display == null)
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
m_ownDisplayRotation = newRotation;
|
||||
|
||||
QtDisplayManager.setApplicationDisplayMetrics(activity, w, h);
|
||||
QtDisplayManager.handleOrientationChanges(activity, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,2 +0,0 @@
|
||||
[orientationChange]
|
||||
android
|
@ -18,6 +18,7 @@ qt_internal_add_test(tst_android
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
Qt::Widgets
|
||||
)
|
||||
|
||||
if(ANDROID)
|
||||
|
@ -12,6 +12,8 @@
|
||||
#include <qpa/qplatformscreen.h>
|
||||
#include <qpa/qplatformnativeinterface.h>
|
||||
#include <QtCore/qdiriterator.h>
|
||||
#include <QWidget>
|
||||
#include <QSignalSpy>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
@ -305,19 +307,37 @@ void tst_Android::orientationChange_data()
|
||||
{
|
||||
QTest::addColumn<int>("nativeOrientation");
|
||||
QTest::addColumn<Qt::ScreenOrientation>("expected");
|
||||
QTest::addColumn<QSize>("screenSize");
|
||||
|
||||
QTest::newRow("Landscape") << 0 << Qt::LandscapeOrientation;
|
||||
QTest::newRow("Portrait") << 1 << Qt::PortraitOrientation;
|
||||
const QSize portraitSize = QGuiApplication::primaryScreen()->size();
|
||||
const QSize landscapeSize = QSize(portraitSize.height(), portraitSize.width());
|
||||
|
||||
QTest::newRow("InvertedLandscape") << 8 << Qt::InvertedLandscapeOrientation << landscapeSize;
|
||||
QTest::newRow("InvertedPortrait") << 9 << Qt::InvertedPortraitOrientation << portraitSize;
|
||||
QTest::newRow("Landscape") << 0 << Qt::LandscapeOrientation << landscapeSize;
|
||||
// Leave Portrait till the end
|
||||
QTest::newRow("Portrait") << 1 << Qt::PortraitOrientation << portraitSize;
|
||||
}
|
||||
|
||||
void tst_Android::orientationChange()
|
||||
{
|
||||
QFETCH(int, nativeOrientation);
|
||||
QFETCH(Qt::ScreenOrientation, expected);
|
||||
QFETCH(QSize, screenSize);
|
||||
|
||||
// For QTBUG-94459 to check that the widget size are consistent after orientation changes
|
||||
QWidget widget;
|
||||
widget.show();
|
||||
|
||||
auto context = QNativeInterface::QAndroidApplication::context();
|
||||
context.callMethod<void>("setRequestedOrientation", nativeOrientation);
|
||||
QTRY_COMPARE(qGuiApp->primaryScreen()->orientation(), expected);
|
||||
|
||||
QScreen *screen = QGuiApplication::primaryScreen();
|
||||
QSignalSpy orientationSpy(screen, SIGNAL(orientationChanged(Qt::ScreenOrientation)));
|
||||
QTRY_COMPARE(screen->orientation(), expected);
|
||||
QCOMPARE(orientationSpy.size(), 1);
|
||||
QCOMPARE(screen->size(), screenSize);
|
||||
QCOMPARE(widget.size(), screen->availableSize());
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_Android)
|
||||
|
Loading…
x
Reference in New Issue
Block a user