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 {
// screen methods
static native void setDisplayMetrics(int screenWidthPixels, int screenHeightPixels,
int availableWidthPixels, int availableHeightPixels);
static native void handleLayoutSizeChanged(int availableWidth, int availableHeight);
static native void handleOrientationChanged(int newRotation, int nativeOrientation);
static native void handleRefreshRateChanged(float refreshRate);
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
static float getXDpi(final DisplayMetrics metrics) {
if (metrics.xdpi < android.util.DisplayMetrics.DENSITY_LOW)

View File

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

View File

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

View File

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

View File

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

View File

@ -499,10 +499,8 @@ void QAndroidPlatformIntegration::setScreenOrientation(Qt::ScreenOrientation cur
void QAndroidPlatformIntegration::flushPendingUpdates()
{
if (m_primaryScreen) {
m_primaryScreen->setSizeParameters(
m_primaryScreen->geometry().size(), m_primaryScreen->availableGeometry());
}
if (m_primaryScreen)
m_primaryScreen->setAvailableGeometry(m_primaryScreen->availableGeometry());
}
#if QT_CONFIG(accessibility)
@ -542,15 +540,6 @@ void QAndroidPlatformIntegration::updateColorScheme(Qt::ColorScheme colorScheme)
[] () { 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)
{
if (m_primaryScreen)
@ -581,10 +570,17 @@ void QAndroidPlatformIntegration::handleScreenChanged(int displayId)
if (it == m_screens.end() || it->second == nullptr) {
handleScreenAdded(displayId);
}
// We do not do anything more here as handling of change of
// rotation and refresh rate is done in QtActivityDelegate java class
// which calls QAndroidPlatformIntegration::setOrientation, and
// QAndroidPlatformIntegration::setRefreshRate accordingly.
if (QAndroidPlatformScreen *screen = it->second) {
QSize size = QAndroidPlatformScreen::sizeForDisplayId(displayId);
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)

View File

@ -66,11 +66,6 @@ public:
void setAvailableGeometry(const QRect &availableGeometry);
void setPhysicalSize(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);
bool isVirtualDesktop() { return true; }

View File

@ -82,14 +82,10 @@ QAndroidPlatformScreen::QAndroidPlatformScreen(const QJniObject &displayObject)
m_name = displayObject.callObjectMethod<jstring>("getName").toString();
m_refreshRate = displayObject.callMethod<jfloat>("getRefreshRate");
m_displayId = displayObject.callMethod<jint>("getDisplayId");
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_size = sizeForDisplayId(m_displayId);
m_availableGeometry = defaultAvailableGeometry();
const auto context = QNativeInterface::QAndroidApplication::context();
const auto resources = context.callMethod<QtJniTypes::Resources>("getResources");
const auto metrics = resources.callMethod<QtJniTypes::DisplayMetrics>("getDisplayMetrics");
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,
// but the results are not consistent with devices specs.
// (https://issuetracker.google.com/issues/194120500)
m_physicalSize.setWidth(qRound(m_size.width() / m_xdpi * 25.4));
m_physicalSize.setHeight(qRound(m_size.height() / m_ydpi * 25.4));
setPhysicalSizeFromPixels(m_size);
if (QNativeInterface::QAndroidApplication::sdkVersion() >= 23) {
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
{
for (QAndroidPlatformWindow *w : m_windowStack) {
@ -213,28 +221,18 @@ void QAndroidPlatformScreen::setPhysicalSize(const QSize &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)
{
m_size = size;
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());
}
QWindowSystemInterface::handleScreenGeometryChange(
QPlatformScreen::screen(), geometry(), availableGeometry());
}
int QAndroidPlatformScreen::displayId() const

View File

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