QGuiApplication on Android can now detect multiple displays

- Extending QtNative.java with access to DisplayManager and get
  details about available displays
- Extending Android Platform Integration with display's list
  handling
- Change QAndroidPlatformScreen to initialize itself from QJniObject
  representation of an android Display object
- Move initialization of Primary display from QAndroidPlatformScreen
  to QAndroidPlatformIntegration

Change-Id: I3d8f97f5cf9f81bbecc8716c25ff323097e57a15
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
Wojciech Błaszak 2022-07-22 13:05:56 +02:00 committed by Petri Virkkunen
parent c7b93d471d
commit fbf586db2c
4 changed files with 102 additions and 56 deletions

View File

@ -8,6 +8,7 @@ import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Semaphore;
import java.util.HashMap;
@ -37,6 +38,8 @@ import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.InputDevice;
import android.view.Display;
import android.hardware.display.DisplayManager;
import android.database.Cursor;
import android.provider.DocumentsContract;
@ -592,6 +595,18 @@ public class QtNative
});
}
public static List<Display> getAvailableDisplays()
{
Context context = getContext();
DisplayManager displayManager =
(DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);
if (displayManager != null) {
Display[] displays = displayManager.getDisplays();
return Arrays.asList(displays);
}
return new ArrayList<Display>();
}
public static boolean startApplication(String params, String mainLib) throws Exception
{
if (params == null)

View File

@ -56,6 +56,11 @@ Qt::ScreenOrientation QAndroidPlatformIntegration::m_nativeOrientation = Qt::Pri
bool QAndroidPlatformIntegration::m_showPasswordEnabled = false;
static bool m_running = false;
Q_DECLARE_JNI_CLASS(QtNative, "org/qtproject/qt/android/QtNative")
Q_DECLARE_JNI_CLASS(Display, "android/view/Display")
Q_DECLARE_JNI_TYPE(List, "Ljava/util/List;")
void *QAndroidPlatformNativeInterface::nativeResourceForIntegration(const QByteArray &resource)
{
if (resource=="JavaVM")
@ -163,10 +168,33 @@ QAndroidPlatformIntegration::QAndroidPlatformIntegration(const QStringList &para
if (Q_UNLIKELY(!eglBindAPI(EGL_OPENGL_ES_API)))
qFatal("Could not bind GL_ES API");
m_primaryScreen = new QAndroidPlatformScreen();
QWindowSystemInterface::handleScreenAdded(m_primaryScreen);
m_primaryScreen->setSizeParameters(m_defaultPhysicalSize, m_defaultScreenSize,
m_defaultAvailableGeometry);
static const int primaryDisplayId = QJniObject::getStaticField<jint>(
QtJniTypes::className<QtJniTypes::Display>(), "DEFAULT_DISPLAY");
const QJniObject nativeDisplaysList = QJniObject::callStaticObjectMethod<QtJniTypes::List>(
QtJniTypes::className<QtJniTypes::QtNative>(),
"getAvailableDisplays");
const int numberOfAvailableDisplays = nativeDisplaysList.callMethod<jint>("size");
for (int i = 0; i < numberOfAvailableDisplays; ++i) {
const QJniObject display =
nativeDisplaysList.callObjectMethod<jobject, jint>("get", jint(i));
const bool isPrimary = (primaryDisplayId == display.callMethod<jint>("getDisplayId"));
auto screen = new QAndroidPlatformScreen(display);
if (isPrimary)
m_primaryScreen = screen;
QWindowSystemInterface::handleScreenAdded(screen, isPrimary);
}
if (numberOfAvailableDisplays == 0) {
// If no displays are found, add a dummy display
auto defaultScreen = new QAndroidPlatformScreen(QJniObject {});
m_primaryScreen = defaultScreen;
QWindowSystemInterface::handleScreenAdded(defaultScreen, true);
}
m_mainThread = QThread::currentThread();

View File

@ -52,11 +52,17 @@ private:
# define PROFILE_SCOPE
#endif
QAndroidPlatformScreen::QAndroidPlatformScreen()
Q_DECLARE_JNI_CLASS(Display, "android/view/Display")
Q_DECLARE_JNI_TYPE(DisplayMode, "Landroid/view/Display$Mode;")
QAndroidPlatformScreen::QAndroidPlatformScreen(const QJniObject &displayObject)
: QObject(), QPlatformScreen()
{
m_availableGeometry = QAndroidPlatformIntegration::m_defaultAvailableGeometry;
m_size = QAndroidPlatformIntegration::m_defaultScreenSize;
m_physicalSize = QAndroidPlatformIntegration::m_defaultPhysicalSize;
// Raster only apps should set QT_ANDROID_RASTER_IMAGE_DEPTH to 16
// is way much faster than 32
if (qEnvironmentVariableIntValue("QT_ANDROID_RASTER_IMAGE_DEPTH") == 16) {
@ -66,54 +72,52 @@ QAndroidPlatformScreen::QAndroidPlatformScreen()
m_format = QImage::Format_ARGB32_Premultiplied;
m_depth = 32;
}
m_physicalSize = QAndroidPlatformIntegration::m_defaultPhysicalSize;
connect(qGuiApp, &QGuiApplication::applicationStateChanged, this, &QAndroidPlatformScreen::applicationStateChanged);
QJniObject activity(QtAndroid::activity());
if (!activity.isValid())
return;
QJniObject display;
if (QNativeInterface::QAndroidApplication::sdkVersion() < 30) {
display = activity.callObjectMethod("getWindowManager", "()Landroid/view/WindowManager;")
.callObjectMethod("getDefaultDisplay", "()Landroid/view/Display;");
} else {
display = activity.callObjectMethod("getDisplay", "()Landroid/view/Display;");
}
if (!display.isValid())
connect(qGuiApp, &QGuiApplication::applicationStateChanged, this,
&QAndroidPlatformScreen::applicationStateChanged);
if (!displayObject.isValid())
return;
m_name = display.callObjectMethod("getName", "()Ljava/lang/String;").toString();
m_refreshRate = display.callMethod<jfloat>("getRefreshRate");
m_size = QSize(displayObject.callMethod<jint>("getWidth"), displayObject.callMethod<jint>("getHeight"));
m_name = displayObject.callObjectMethod<jstring>("getName").toString();
m_refreshRate = displayObject.callMethod<jfloat>("getRefreshRate");
if (QNativeInterface::QAndroidApplication::sdkVersion() < 23) {
m_modes << Mode { .size = m_physicalSize.toSize(), .refreshRate = m_refreshRate };
return;
if (QNativeInterface::QAndroidApplication::sdkVersion() >= 23) {
const QJniObject currentMode = displayObject.callObjectMethod<QtJniTypes::DisplayMode>("getMode");
const jint currentModeId = currentMode.callMethod<jint>("getModeId");
const QJniObject supportedModes = displayObject.callObjectMethod<QtJniTypes::DisplayMode[]>(
"getSupportedModes");
const auto modeArray = jobjectArray(supportedModes.object());
QJniEnvironment env;
const auto size = env->GetArrayLength(modeArray);
for (jsize i = 0; i < size; ++i) {
const auto mode = QJniObject::fromLocalRef(env->GetObjectArrayElement(modeArray, i));
const int physicalWidth = mode.callMethod<jint>("getPhysicalWidth");
const int physicalHeight = mode.callMethod<jint>("getPhysicalHeight");
if (currentModeId == mode.callMethod<jint>("getModeId")) {
m_currentMode = i;
m_physicalSize = QSize {
physicalWidth,
physicalHeight
};
}
m_modes << QPlatformScreen::Mode {
.size = QSize { physicalWidth, physicalHeight },
.refreshRate = mode.callMethod<jfloat>("getRefreshRate")
};
}
}
QJniEnvironment env;
const jint currentMode = display.callObjectMethod("getMode", "()Landroid/view/Display$Mode;")
.callMethod<jint>("getModeId");
const auto modes = display.callObjectMethod("getSupportedModes",
"()[Landroid/view/Display$Mode;");
const auto modesArray = jobjectArray(modes.object());
const auto sz = env->GetArrayLength(modesArray);
for (jsize i = 0; i < sz; ++i) {
auto mode = QJniObject::fromLocalRef(env->GetObjectArrayElement(modesArray, i));
if (currentMode == mode.callMethod<jint>("getModeId"))
m_currentMode = m_modes.size();
m_modes << Mode { .size = QSize { mode.callMethod<jint>("getPhysicalHeight"),
mode.callMethod<jint>("getPhysicalWidth") },
.refreshRate = mode.callMethod<jfloat>("getRefreshRate") };
}
if (m_modes.isEmpty())
m_modes << Mode { .size = m_physicalSize.toSize(), .refreshRate = m_refreshRate };
}
QAndroidPlatformScreen::~QAndroidPlatformScreen()
{
if (m_id != -1) {
QtAndroid::destroySurface(m_id);
if (m_surfaceId != -1) {
QtAndroid::destroySurface(m_surfaceId);
m_surfaceWaitCondition.wakeOne();
releaseSurface();
}
@ -304,9 +308,9 @@ void QAndroidPlatformScreen::setAvailableGeometry(const QRect &rect)
}
}
if (m_id != -1) {
if (m_surfaceId != -1) {
releaseSurface();
QtAndroid::setSurfaceGeometry(m_id, rect);
QtAndroid::setSurfaceGeometry(m_surfaceId, rect);
}
}
@ -317,8 +321,8 @@ void QAndroidPlatformScreen::applicationStateChanged(Qt::ApplicationState state)
if (state <= Qt::ApplicationHidden) {
lockSurface();
QtAndroid::destroySurface(m_id);
m_id = -1;
QtAndroid::destroySurface(m_surfaceId);
m_surfaceId = -1;
releaseSurface();
unlockSurface();
}
@ -361,17 +365,17 @@ void QAndroidPlatformScreen::doRedraw(QImage* screenGrabImage)
}
if (!hasVisibleRasterWindows) {
lockSurface();
if (m_id != -1) {
QtAndroid::destroySurface(m_id);
if (m_surfaceId != -1) {
QtAndroid::destroySurface(m_surfaceId);
releaseSurface();
m_id = -1;
m_surfaceId = -1;
}
unlockSurface();
return;
}
QMutexLocker lock(&m_surfaceMutex);
if (m_id == -1 && m_rasterSurfaces) {
m_id = QtAndroid::createSurface(this, geometry(), true, m_depth);
if (m_surfaceId == -1 && m_rasterSurfaces) {
m_surfaceId = QtAndroid::createSurface(this, geometry(), true, m_depth);
AndroidDeadlockProtector protector;
if (!protector.acquire())
return;

View File

@ -24,7 +24,7 @@ class QAndroidPlatformScreen: public QObject, public QPlatformScreen, public And
{
Q_OBJECT
public:
QAndroidPlatformScreen();
QAndroidPlatformScreen(const QJniObject &displayObject);
~QAndroidPlatformScreen();
QRect geometry() const override { return QRect(QPoint(), m_size); }
@ -38,7 +38,6 @@ public:
int currentMode() const override { return m_currentMode; }
int preferredMode() const override { return m_currentMode; }
qreal refreshRate() const override { return m_refreshRate; }
inline QWindow *topWindow() const;
QWindow *topLevelAt(const QPoint & p) const override;
@ -94,7 +93,7 @@ private slots:
void doRedraw(QImage *screenGrabImage = nullptr);
private:
int m_id = -1;
int m_surfaceId = -1;
QAtomicInt m_rasterSurfaces = 0;
ANativeWindow* m_nativeSurface = nullptr;
QWaitCondition m_surfaceWaitCondition;