Android: fix 180 degree orientation issue

On android documentation orientation changes are sent via
onConfigurationChanged callback. Previous implementation
based orientation detection to onSizeChanged callback.
That callback is not called when orientation turns 180 degrees.
i.e. between landscape and inverted landscape.
This fix adds detection to on onConfigurationChanged to catch
those cases.

Fixes: QTBUG-118887
Fixes: QTBUG-118236
Pick-to: 6.5
Change-Id: Ie2f81798de97e460de839f7ebfde9a9efa25909f
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
(cherry picked from commit 6209079c7a34c4656e6e110ae5194feb19f6134a)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Lauri Pohjanheimo 2024-03-11 13:33:05 +02:00 committed by Qt Cherry-pick Bot
parent 61885e26e4
commit 7aeffcebbd
4 changed files with 68 additions and 33 deletions

View File

@ -113,7 +113,7 @@ public class QtActivityDelegate extends QtActivityDelegateBase
m_activity.setContentView(m_layout,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
QtDisplayManager.handleOrientationChanges(m_activity, false);
QtDisplayManager.handleOrientationChanges(m_activity);
handleUiModeChange(m_activity.getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);

View File

@ -64,7 +64,6 @@ class QtDisplayManager {
@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();
@ -80,35 +79,17 @@ 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)
static void handleOrientationChanges(Activity activity)
{
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;
}
int nativeOrientation = getNativeOrientation(activity, currentRotation);
QtDisplayManager.handleOrientationChanged(currentRotation, nativeOrientation);
m_previousRotation = currentRotation;
}
private static int getDisplayRotation(Activity activity) {
public static int getDisplayRotation(Activity activity) {
Display display = Build.VERSION.SDK_INT < Build.VERSION_CODES.R ?
activity.getWindowManager().getDefaultDisplay() :
activity.getDisplay();

View File

@ -9,6 +9,8 @@ import android.content.Context;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.content.res.Configuration;
import android.view.Surface;
/**
A layout which corresponds to one Activity, i.e. is the root layout where the top level window
@ -16,10 +18,10 @@ import android.view.Display;
*/
public class QtRootLayout extends QtLayout
{
private int m_activityDisplayRotation = -1;
private int m_ownDisplayRotation = -1;
private int m_nativeOrientation = -1;
private int m_previousRotation = -1;
public QtRootLayout(Context context)
{
@ -67,8 +69,30 @@ public class QtRootLayout extends QtLayout
// a bit later.
return;
}
QtDisplayManager.setApplicationDisplayMetrics(activity, w, h);
QtDisplayManager.handleOrientationChanges(activity, true);
QtDisplayManager.handleOrientationChanges(activity);
}
@Override
public void onConfigurationChanged(Configuration configuration)
{
Context context = getContext();
if (context instanceof Activity) {
Activity activity = (Activity)context;
//if orientation change is betwen invertedPortrait and portrait or
//invertedLandscape and landscape, we do not get sizeChanged callback.
int rotation = QtDisplayManager.getDisplayRotation(activity);
if (isSameSizeForOrientations(rotation, m_previousRotation))
QtDisplayManager.handleOrientationChanges(activity);
m_previousRotation = rotation;
}
}
public 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);
}
}

View File

@ -312,11 +312,39 @@ void tst_Android::orientationChange_data()
const QSize portraitSize = QGuiApplication::primaryScreen()->size();
const QSize landscapeSize = QSize(portraitSize.height(), portraitSize.width());
// Rotations without 180 degree or inverted portrait, assuming that the device is in portrait
// position. These are ok for Android 6(API 23), 8 (API 27) and 14 (API 34)
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;
QTest::newRow("Landscape") << 0 << Qt::LandscapeOrientation << landscapeSize;
QTest::newRow("Portrait2") << 1 << Qt::PortraitOrientation << portraitSize;
// Rotations over inverted portrait doing only 90 degree turns.
QTest::newRow("InvertedLandscape2") << 8 << Qt::InvertedLandscapeOrientation << landscapeSize;
QTest::newRow("InvertedPortrait") << 9 << Qt::InvertedPortraitOrientation << portraitSize;
QTest::newRow("Landscape2") << 0 << Qt::LandscapeOrientation << landscapeSize;
QTest::newRow("InvertedPortrait2") << 9 << Qt::InvertedPortraitOrientation << portraitSize;
QTest::newRow("InvertedLandscape3") << 8 << Qt::InvertedLandscapeOrientation << landscapeSize;
// Rotations with 180 degree turns.
// Android 6 (API23) Does not understand these transitions.
if (QNativeInterface::QAndroidApplication::sdkVersion() > __ANDROID_API_M__) {
QTest::newRow("Landscape3") << 0 << Qt::LandscapeOrientation << landscapeSize;
QTest::newRow("InvertedLandscape4")
<< 8 << Qt::InvertedLandscapeOrientation << landscapeSize;
QTest::newRow("Portrait3") << 1 << Qt::PortraitOrientation << portraitSize;
} else {
qWarning() << "180 degree turn rotation test cases are not run on Android 6 (API 23) and "
"below.";
}
// Android 8 (API 27) does not understand portrait-'inverted portrait'-portrait transition.
if (QNativeInterface::QAndroidApplication::sdkVersion() > __ANDROID_API_O_MR1__) {
QTest::newRow("InvertedPortrait3") << 9 << Qt::InvertedPortraitOrientation << portraitSize;
QTest::newRow("Portrait4") << 1 << Qt::PortraitOrientation << portraitSize;
} else {
qWarning() << "Portrait-'Inverted portrait'-Portrait rotation test cases are not run on "
"Android 8 (API 27) and below.";
}
}
void tst_Android::orientationChange()
@ -329,11 +357,13 @@ void tst_Android::orientationChange()
QWidget widget;
widget.show();
QScreen *screen = QGuiApplication::primaryScreen();
QSignalSpy orientationSpy(screen, SIGNAL(orientationChanged(Qt::ScreenOrientation)));
auto context = QNativeInterface::QAndroidApplication::context();
context.callMethod<void>("setRequestedOrientation", nativeOrientation);
QScreen *screen = QGuiApplication::primaryScreen();
QSignalSpy orientationSpy(screen, SIGNAL(orientationChanged(Qt::ScreenOrientation)));
orientationSpy.wait();
QTRY_COMPARE(screen->orientation(), expected);
QCOMPARE(orientationSpy.size(), 1);
QCOMPARE(screen->size(), screenSize);