Android: separate setting screen size from setDisplayMetrics()

The screen size doens't need to be updated each time the layout size is
changed, they don't always happen together. The screen size change
happens less often than the layout/available size. Thus, do handle only
layout change under setDisplayMetrics() and move screen size changes to
onDisplayChanged() callback where it logically belongs.

Along the way rename setDisplayMetrics() to handleLayoutSizeChanged()
to reflect what it actually does now after the cleanups.

Task-number: QTBUG-132720
Change-Id: I9dfcfe4a0d2c0be36fb0a5fbf5975c7d466ddef2
Reviewed-by: Petri Virkkunen <petri.virkkunen@qt.io>
This commit is contained in:
Assam Boudjelthia 2025-01-06 21:21:21 +02:00
parent f346a6065d
commit 7dcf58eedb
9 changed files with 56 additions and 78 deletions

View File

@ -32,8 +32,7 @@ import android.content.res.Resources.Theme;
class QtDisplayManager { class QtDisplayManager {
// screen methods // screen methods
static native void setDisplayMetrics(int screenWidthPixels, int screenHeightPixels, static native void handleLayoutSizeChanged(int availableWidth, int availableHeight);
int availableWidthPixels, int availableHeightPixels);
static native void handleOrientationChanged(int newRotation, int nativeOrientation); static native void handleOrientationChanged(int newRotation, int nativeOrientation);
static native void handleRefreshRateChanged(float refreshRate); static native void handleRefreshRateChanged(float refreshRate);
static native void handleUiDarkModeChanged(int newUiMode); static native void handleUiDarkModeChanged(int newUiMode);
@ -290,15 +289,6 @@ class QtDisplayManager {
} }
} }
static void setApplicationDisplayMetrics(Activity activity, int width, int height)
{
if (activity == null)
return;
Size displaySize = getDisplaySize(activity, QtDisplayManager.getDisplay(activity));
setDisplayMetrics(displaySize.getWidth(), displaySize.getHeight(), width, height);
}
@UsedFromNativeCode @UsedFromNativeCode
static float getXDpi(final DisplayMetrics metrics) { static float getXDpi(final DisplayMetrics metrics) {
if (metrics.xdpi < android.util.DisplayMetrics.DENSITY_LOW) if (metrics.xdpi < android.util.DisplayMetrics.DENSITY_LOW)

View File

@ -113,8 +113,7 @@ class QtEmbeddedDelegate extends QtActivityDelegateBase
if (ready) { if (ready) {
QtNative.runAction(() -> { QtNative.runAction(() -> {
DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics(); DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
QtDisplayManager.setApplicationDisplayMetrics(m_activity, metrics.widthPixels, QtDisplayManager.handleLayoutSizeChanged(metrics.widthPixels, metrics.heightPixels);
metrics.heightPixels);
}); });
} }
} }

View File

@ -23,10 +23,10 @@ class QtRootLayout extends QtLayout
protected void onSizeChanged(int w, int h, int oldw, int oldh) protected void onSizeChanged(int w, int h, int oldw, int oldh)
{ {
Activity activity = (Activity) getContext(); Activity activity = (Activity) getContext();
if (activity == null) if (activity == null || (w == oldw && h == oldh))
return; return;
QtDisplayManager.setApplicationDisplayMetrics(activity, w, h); QtDisplayManager.handleLayoutSizeChanged(w, h);
} }
@Override @Override

View File

@ -40,7 +40,7 @@ class QtServiceEmbeddedDelegate implements QtEmbeddedViewInterface, QtNative.App
final int maxWidth = metrics.widthPixels; final int maxWidth = metrics.widthPixels;
final int maxHeight = metrics.heightPixels; final int maxHeight = metrics.heightPixels;
QtDisplayManager.setDisplayMetrics(maxWidth, maxHeight, maxWidth, maxHeight); QtDisplayManager.handleLayoutSizeChanged(maxWidth, maxHeight);
QtDisplayManager.updateRefreshRate(m_service); QtDisplayManager.updateRefreshRate(m_service);
QtDisplayManager.handleScreenDensityChanged(metrics.density); QtDisplayManager.handleScreenDensityChanged(metrics.density);

View File

@ -565,24 +565,24 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
sem_post(&m_exitSemaphore); sem_post(&m_exitSemaphore);
} }
static void setDisplayMetrics(JNIEnv * /*env*/, jclass /*clazz*/, static void handleLayoutSizeChanged(JNIEnv * /*env*/, jclass /*clazz*/,
jint screenWidthPixels, jint screenHeightPixels, jint availableWidth, jint availableHeight)
jint availableWidthPixels, jint availableHeightPixels)
{ {
m_availableWidthPixels = availableWidthPixels; if (m_availableWidthPixels == availableWidth && m_availableHeightPixels == availableHeight)
m_availableHeightPixels = availableHeightPixels; return;
const QSize screenSize(screenWidthPixels, screenHeightPixels); m_availableWidthPixels = availableWidth;
// available geometry always starts from top left m_availableHeightPixels = availableHeight;
const QRect availableGeometry(0, 0, availableWidthPixels, availableHeightPixels);
QMutexLocker lock(&m_platformMutex); QMutexLocker lock(&m_platformMutex);
// available geometry always starts from top left
const QRect availableGeometry(0, 0, availableWidth, availableHeight);
if (m_androidPlatformIntegration) if (m_androidPlatformIntegration)
m_androidPlatformIntegration->setScreenSizeParameters(screenSize, availableGeometry); m_androidPlatformIntegration->setAvailableGeometry(availableGeometry);
else if (QAndroidPlatformScreen::defaultAvailableGeometry().isNull()) else if (QAndroidPlatformScreen::defaultAvailableGeometry().isNull())
QAndroidPlatformScreen::defaultAvailableGeometry() = availableGeometry; QAndroidPlatformScreen::defaultAvailableGeometry() = availableGeometry;
} }
Q_DECLARE_JNI_NATIVE_METHOD(setDisplayMetrics) Q_DECLARE_JNI_NATIVE_METHOD(handleLayoutSizeChanged)
static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state) static void updateApplicationState(JNIEnv */*env*/, jobject /*thiz*/, jint state)
{ {
@ -785,7 +785,7 @@ static bool registerNatives(QJniEnvironment &env)
success &= env.registerNativeMethods( success &= env.registerNativeMethods(
QtJniTypes::Traits<QtJniTypes::QtDisplayManager>::className(), QtJniTypes::Traits<QtJniTypes::QtDisplayManager>::className(),
{ {
Q_JNI_NATIVE_METHOD(setDisplayMetrics), Q_JNI_NATIVE_METHOD(handleLayoutSizeChanged),
Q_JNI_NATIVE_METHOD(handleOrientationChanged), Q_JNI_NATIVE_METHOD(handleOrientationChanged),
Q_JNI_NATIVE_METHOD(handleRefreshRateChanged), Q_JNI_NATIVE_METHOD(handleRefreshRateChanged),
Q_JNI_NATIVE_METHOD(handleScreenAdded), Q_JNI_NATIVE_METHOD(handleScreenAdded),

View File

@ -499,10 +499,8 @@ void QAndroidPlatformIntegration::setScreenOrientation(Qt::ScreenOrientation cur
void QAndroidPlatformIntegration::flushPendingUpdates() void QAndroidPlatformIntegration::flushPendingUpdates()
{ {
if (m_primaryScreen) { if (m_primaryScreen)
m_primaryScreen->setSizeParameters( m_primaryScreen->setAvailableGeometry(m_primaryScreen->availableGeometry());
m_primaryScreen->geometry().size(), m_primaryScreen->availableGeometry());
}
} }
#if QT_CONFIG(accessibility) #if QT_CONFIG(accessibility)
@ -542,15 +540,6 @@ void QAndroidPlatformIntegration::updateColorScheme(Qt::ColorScheme colorScheme)
[] () { QAndroidPlatformTheme::instance()->updateColorScheme();}); [] () { QAndroidPlatformTheme::instance()->updateColorScheme();});
} }
void QAndroidPlatformIntegration::setScreenSizeParameters(const QSize &screenSize,
const QRect &availableGeometry)
{
if (m_primaryScreen) {
QMetaObject::invokeMethod(m_primaryScreen, "setSizeParameters", Qt::AutoConnection,
Q_ARG(QSize, screenSize), Q_ARG(QRect, availableGeometry));
}
}
void QAndroidPlatformIntegration::setRefreshRate(qreal refreshRate) void QAndroidPlatformIntegration::setRefreshRate(qreal refreshRate)
{ {
if (m_primaryScreen) if (m_primaryScreen)
@ -581,10 +570,17 @@ void QAndroidPlatformIntegration::handleScreenChanged(int displayId)
if (it == m_screens.end() || it->second == nullptr) { if (it == m_screens.end() || it->second == nullptr) {
handleScreenAdded(displayId); handleScreenAdded(displayId);
} }
// We do not do anything more here as handling of change of
// rotation and refresh rate is done in QtActivityDelegate java class if (QAndroidPlatformScreen *screen = it->second) {
// which calls QAndroidPlatformIntegration::setOrientation, and QSize size = QAndroidPlatformScreen::sizeForDisplayId(displayId);
// QAndroidPlatformIntegration::setRefreshRate accordingly. if (screen->geometry().size() != size) {
screen->setPhysicalSizeFromPixels(size);
screen->setSize(size);
}
}
// We do not do handle changes in rotation, refresh rate and density
// as they are done under QtDisplayManager.
} }
void QAndroidPlatformIntegration::handleScreenRemoved(int displayId) void QAndroidPlatformIntegration::handleScreenRemoved(int displayId)

View File

@ -66,11 +66,6 @@ public:
void setAvailableGeometry(const QRect &availableGeometry); void setAvailableGeometry(const QRect &availableGeometry);
void setPhysicalSize(int width, int height); void setPhysicalSize(int width, int height);
void setScreenSize(int width, int height); void setScreenSize(int width, int height);
// The 3 methods above were replaced by a new one, so that we could have
// a better control over "geometry changed" event handling. Technically
// they are no longer used and can be removed. Not doing it now, because
// I'm not sure if it might be helpful to have them or not.
void setScreenSizeParameters(const QSize &screenSize, const QRect &availableGeometry);
void setRefreshRate(qreal refreshRate); void setRefreshRate(qreal refreshRate);
bool isVirtualDesktop() { return true; } bool isVirtualDesktop() { return true; }

View File

@ -82,14 +82,10 @@ QAndroidPlatformScreen::QAndroidPlatformScreen(const QJniObject &displayObject)
m_name = displayObject.callObjectMethod<jstring>("getName").toString(); m_name = displayObject.callObjectMethod<jstring>("getName").toString();
m_refreshRate = displayObject.callMethod<jfloat>("getRefreshRate"); m_refreshRate = displayObject.callMethod<jfloat>("getRefreshRate");
m_displayId = displayObject.callMethod<jint>("getDisplayId"); m_displayId = displayObject.callMethod<jint>("getDisplayId");
m_size = sizeForDisplayId(m_displayId);
const auto context = QNativeInterface::QAndroidApplication::context();
const auto sizeObj = QtJniTypes::QtDisplayManager::callStaticMethod<QtJniTypes::Size>(
"getDisplaySize", context,
displayObject.object<QtJniTypes::Display>());
m_size = QSize(sizeObj.callMethod<int>("getWidth"), sizeObj.callMethod<int>("getHeight"));
m_availableGeometry = defaultAvailableGeometry(); m_availableGeometry = defaultAvailableGeometry();
const auto context = QNativeInterface::QAndroidApplication::context();
const auto resources = context.callMethod<QtJniTypes::Resources>("getResources"); const auto resources = context.callMethod<QtJniTypes::Resources>("getResources");
const auto metrics = resources.callMethod<QtJniTypes::DisplayMetrics>("getDisplayMetrics"); const auto metrics = resources.callMethod<QtJniTypes::DisplayMetrics>("getDisplayMetrics");
m_xdpi = QtJniTypes::QtDisplayManager::callStaticMethod<jfloat>("getXDpi", metrics); m_xdpi = QtJniTypes::QtDisplayManager::callStaticMethod<jfloat>("getXDpi", metrics);
@ -98,8 +94,7 @@ QAndroidPlatformScreen::QAndroidPlatformScreen(const QJniObject &displayObject)
// Potentially densityDpi could be used instead of xpdi/ydpi to do the calculation, // Potentially densityDpi could be used instead of xpdi/ydpi to do the calculation,
// but the results are not consistent with devices specs. // but the results are not consistent with devices specs.
// (https://issuetracker.google.com/issues/194120500) // (https://issuetracker.google.com/issues/194120500)
m_physicalSize.setWidth(qRound(m_size.width() / m_xdpi * 25.4)); setPhysicalSizeFromPixels(m_size);
m_physicalSize.setHeight(qRound(m_size.height() / m_ydpi * 25.4));
if (QNativeInterface::QAndroidApplication::sdkVersion() >= 23) { if (QNativeInterface::QAndroidApplication::sdkVersion() >= 23) {
const QJniObject currentMode = displayObject.callObjectMethod<QtJniTypes::DisplayMode>("getMode"); const QJniObject currentMode = displayObject.callObjectMethod<QtJniTypes::DisplayMode>("getMode");
@ -126,6 +121,19 @@ QAndroidPlatformScreen::~QAndroidPlatformScreen()
{ {
} }
QSize QAndroidPlatformScreen::sizeForDisplayId(int displayId)
{
using namespace QtJniTypes;
const auto context = QNativeInterface::QAndroidApplication::context();
const auto display = QtDisplayManager::callStaticMethod<Display>(
"getDisplay", context, displayId);
const auto sizeObj = QtDisplayManager::callStaticMethod<Size>(
"getDisplaySize", context, display);
return QSize(sizeObj.callMethod<int>("getWidth"), sizeObj.callMethod<int>("getHeight"));
}
QWindow *QAndroidPlatformScreen::topVisibleWindow() const QWindow *QAndroidPlatformScreen::topVisibleWindow() const
{ {
for (QAndroidPlatformWindow *w : m_windowStack) { for (QAndroidPlatformWindow *w : m_windowStack) {
@ -213,28 +221,18 @@ void QAndroidPlatformScreen::setPhysicalSize(const QSize &size)
m_physicalSize = size; m_physicalSize = size;
} }
void QAndroidPlatformScreen::setPhysicalSizeFromPixels(const QSize &size)
{
m_physicalSize = QSize(
qRound(double(size.width()) / m_xdpi * 25.4),
qRound(double(size.height()) / m_ydpi * 25.4));
}
void QAndroidPlatformScreen::setSize(const QSize &size) void QAndroidPlatformScreen::setSize(const QSize &size)
{ {
m_size = size; m_size = size;
QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(), availableGeometry()); QWindowSystemInterface::handleScreenGeometryChange(
} QPlatformScreen::screen(), geometry(), availableGeometry());
void QAndroidPlatformScreen::setSizeParameters(const QSize &size, const QRect &availableGeometry)
{
// The goal of this method is to set all geometry-related parameters
// at the same time and generate only one screen geometry change event.
m_size = size;
m_physicalSize = QSize(qRound(double(size.width()) / m_xdpi * 25.4),
qRound(double(size.height()) / m_ydpi * 25.4));
// If available geometry has changed, the event will be handled in
// setAvailableGeometry. Otherwise we need to explicitly handle it to
// retain the behavior, because setSize() does the handling unconditionally.
if (m_availableGeometry != availableGeometry) {
setAvailableGeometry(availableGeometry);
} else {
QWindowSystemInterface::handleScreenGeometryChange(QPlatformScreen::screen(), geometry(),
this->availableGeometry());
}
} }
int QAndroidPlatformScreen::displayId() const int QAndroidPlatformScreen::displayId() const

View File

@ -17,7 +17,6 @@ QT_BEGIN_NAMESPACE
class QAndroidPlatformWindow; class QAndroidPlatformWindow;
class QAndroidPlatformScreen : public QObject, class QAndroidPlatformScreen : public QObject,
public QPlatformScreen, public QPlatformScreen,
public QNativeInterface::QAndroidScreen public QNativeInterface::QAndroidScreen
@ -49,12 +48,13 @@ public:
int displayId() const override; int displayId() const override;
static QRect &defaultAvailableGeometry(); static QRect &defaultAvailableGeometry();
static QSize sizeForDisplayId(int displayId);
public slots: public slots:
void setPhysicalSize(const QSize &size); void setPhysicalSize(const QSize &size);
void setPhysicalSizeFromPixels(const QSize &size);
void setAvailableGeometry(const QRect &rect); void setAvailableGeometry(const QRect &rect);
void setSize(const QSize &size); void setSize(const QSize &size);
void setSizeParameters(const QSize &size, const QRect &availableGeometry);
void setRefreshRate(qreal refreshRate); void setRefreshRate(qreal refreshRate);
void setOrientation(Qt::ScreenOrientation orientation); void setOrientation(Qt::ScreenOrientation orientation);