12#include <qguiapplication.h>
13#include <qpa/qwindowsysteminterface.h>
14#include <private/qhighdpiscaling_p.h>
15#include <private/qjnihelpers_p.h>
23Q_DECLARE_JNI_CLASS(QtWindowInterface,
"org/qtproject/qt/android/QtWindowInterface")
24Q_DECLARE_JNI_CLASS(QtInputInterface,
"org/qtproject/qt/android/QtInputInterface")
25Q_DECLARE_JNI_CLASS(QtInputConnectionListener,
26 "org/qtproject/qt/android/QtInputConnection$QtInputConnectionListener")
27Q_DECLARE_JNI_CLASS(QtDisplayManager,
"org/qtproject/qt/android/QtWindowInterface")
28Q_DECLARE_JNI_CLASS(QtWindowInsetsController,
"org/qtproject/qt/android/QtWindowInsetsController")
30QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
31 : QPlatformWindow(window), m_nativeQtWindow(
nullptr),
32 m_surfaceContainerType(SurfaceContainer::TextureView), m_nativeParentQtWindow(
nullptr),
33 m_androidSurfaceObject(
nullptr)
35 if (window->surfaceType() == QSurface::RasterSurface)
36 window->setSurfaceType(QSurface::OpenGLSurface);
44 QWindow *window = QPlatformWindow::window();
48 if (!androidParent->isEmbeddingContainer())
49 m_nativeParentQtWindow = androidParent->nativeWindow();
53 QtJniTypes::QtInputConnectionListener listener =
54 reg->callInterface<QtJniTypes::QtInputInterface, QtJniTypes::QtInputConnectionListener>(
55 "getInputConnectionListener");
57 m_nativeQtWindow = QJniObject::construct<QtJniTypes::QtWindow>(
58 QNativeInterface::QAndroidApplication::context(),
59 isForeignWindow(), m_nativeParentQtWindow, listener);
60 m_nativeViewId = m_nativeQtWindow.callMethod<jint>(
"getId");
64 m_isRaster = window->surfaceType() == QSurface::RasterSurface;
67 const bool forceMaximize = window->windowStates() & (Qt::WindowMaximized | Qt::WindowFullScreen);
68 const QRect nativeScreenGeometry = platformScreen()->availableGeometry();
70 setGeometry(nativeScreenGeometry);
72 const QRect requestedNativeGeometry = QHighDpi::toNativePixels(window->geometry(), window);
73 const QRect availableDeviceIndependentGeometry = (window->parent())
74 ? window->parent()->geometry()
75 : QHighDpi::fromNativePixels(nativeScreenGeometry, window);
77 const QRect finalNativeGeometry = QPlatformWindow::initialGeometry(
78 window, requestedNativeGeometry, availableDeviceIndependentGeometry.width(),
79 availableDeviceIndependentGeometry.height());
80 setGeometry(finalNativeGeometry);
83 if (window->isTopLevel())
84 platformScreen()->addWindow(
this);
86 static bool ok =
false;
87 static const int value = qEnvironmentVariableIntValue(
"QT_ANDROID_SURFACE_CONTAINER_TYPE", &ok);
92 }
else if (platformScreen()->windows().size() <= 1) {
98 qCDebug(lcQpaWindow) <<
"Window" <<
m_nativeViewId <<
"using surface container type"
104 const auto guard = destructionGuard();
105 if (window()->isTopLevel())
106 platformScreen()->removeWindow(
this);
112 if (m_nativeParentQtWindow.isValid()) {
113 m_nativeParentQtWindow.callMethod<
void>(
"bringChildToBack", nativeViewId());
116 platformScreen()->lower(
this);
121 if (m_nativeParentQtWindow.isValid()) {
122 m_nativeParentQtWindow.callMethod<
void>(
"bringChildToFront", nativeViewId());
125 applySystemUi(window()->windowStates(), window()->flags());
126 platformScreen()->raise(
this);
131 return m_safeAreaMargins;
136 m_safeAreaMargins = safeMargins;
142 Q_ASSERT(m_nativeQtWindow.isValid());
148 if (!rect.isNull()) {
154 m_nativeQtWindow.callMethod<
void>(
"setGeometry", x, y, w, h);
157 QWindowSystemInterface::handleGeometryChange(window(), rect);
165 if (window()->isTopLevel()) {
166 if (!visible && window() ==
qGuiApp->focusWindow()) {
167 platformScreen()->topVisibleWindowChanged();
169 const Qt::WindowStates states = window()->windowStates();
170 const Qt::WindowFlags flags = window()->flags();
171 applySystemUi(states, flags);
172 if (states & Qt::WindowFullScreen || flags & Qt::ExpandedClientAreaHint)
173 setGeometry(platformScreen()->geometry());
174 else if (states & Qt::WindowMaximized)
175 setGeometry(platformScreen()->availableGeometry());
180 m_nativeQtWindow.callMethod<
void>(
"setVisible", visible);
182 if (geometry().isEmpty() || screen()->availableGeometry().isEmpty())
185 QPlatformWindow::setVisible(visible);
190 QPlatformWindow::setWindowState(state);
192 if (window()->isVisible())
193 applySystemUi(state, window()->flags());
198 QPlatformWindow::setWindowFlags(flags);
200 if (window()->isVisible())
201 applySystemUi(window()->windowStates(), flags);
206 using namespace QtJniTypes;
210 if (androidWindow->isEmbeddingContainer())
213 if (!m_nativeParentQtWindow.isValid())
214 platformScreen()->removeWindow(
this);
216 const QtWindow parentWindow = androidWindow->nativeWindow();
218 m_nativeQtWindow.callMethod<
void, QtWindow>(
"setParent", parentWindow.object());
219 m_nativeParentQtWindow = parentWindow;
221 m_nativeQtWindow.callMethod<
void, QtWindow>(
"setParent",
nullptr);
222 m_nativeParentQtWindow = QJniObject();
223 platformScreen()->addWindow(
this);
229 return m_nativeQtWindow.isValid() ?
reinterpret_cast<WId>(m_nativeQtWindow.object()) : 0L;
248 if (m_nativeParentQtWindow.isValid())
249 QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason);
256enum class SystemUiMode { Expanded, FullScreen };
258std::optional<SystemUiMode> systemUiModeFor(Qt::WindowStates states, Qt::WindowFlags flags)
260 if (states & Qt::WindowFullScreen)
261 return SystemUiMode::FullScreen;
262 if (flags & Qt::ExpandedClientAreaHint)
263 return SystemUiMode::Expanded;
269std::optional<SystemUiMode> g_ownedSystemUiMode;
271void applySystemUiMode(
std::optional<SystemUiMode> mode)
273 g_ownedSystemUiMode = mode;
275 auto iface =
qGuiApp->nativeInterface<QNativeInterface::QAndroidApplication>();
276 iface->runOnAndroidMainThread([mode, iface]() {
277 using namespace QtJniTypes;
278 auto activity = iface->context().object<Activity>();
279 if (mode == SystemUiMode::FullScreen)
280 QtWindowInsetsController::callStaticMethod(
"showFullScreen", activity);
281 else if (mode == SystemUiMode::Expanded)
282 QtWindowInsetsController::callStaticMethod(
"showExpanded", activity);
284 QtWindowInsetsController::callStaticMethod(
"showNormal", activity);
291 const bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window;
292 if (isNonRegularWindow)
295 const std::optional<SystemUiMode> mode = systemUiModeFor(states, flags);
296 if (mode != g_ownedSystemUiMode)
297 applySystemUiMode(mode);
310 QMetaObject::invokeMethod(
qGuiApp, [] {
311 if (g_ownedSystemUiMode)
312 applySystemUiMode(g_ownedSystemUiMode);
318 m_nativeQtWindow.callMethod<
void>(
"updateFocusedEditText");
323 return qApp->applicationState() > Qt::ApplicationHidden
324 && window()->isVisible()
325 && !window()->geometry().isEmpty();
332 region = QRect(QPoint(), geometry().size());
334 QWindowSystemInterface::handleExposeEvent(window(), region);
335 QWindowSystemInterface::flushWindowSystemEvents();
340 const bool windowStaysOnTop =
bool(window()->flags() & Qt::WindowStaysOnTopHint);
341 const bool isOpaque = !format().hasAlpha() && qFuzzyCompare(window()->opacity(), qreal(1.0));
343 m_nativeQtWindow.callMethod<
void>(
"createSurface", windowStaysOnTop, 32, isOpaque,
344 m_surfaceContainerType);
351 m_nativeQtWindow.callMethod<
void>(
"destroySurface");
359 const bool surfaceIsValid = surface.isValid();
360 qCDebug(lcQpaWindow) <<
"onSurfaceChanged(): valid Surface received" << surfaceIsValid;
361 m_androidSurfaceObject = surface;
362 if (surfaceIsValid) {
364 m_surfaceWaitCondition.wakeOne();
376 QRect availableGeometry = screen()->availableGeometry();
377 if (!geometry().isNull() && !availableGeometry.isNull()) {
378 QWindowSystemInterface::handleExposeEvent(window(),
379 QRegion(QRect(QPoint(), geometry().size())));
385 QWindow *modalWindow = QGuiApplication::modalWindow();
386 return modalWindow && modalWindow != window();
397 QtJniTypes::Surface surface)
405 const QList<QWindow*> windows =
qGuiApp->allWindows();
406 for (QWindow * window : windows) {
407 if (!window->handle())
409 QAndroidPlatformWindow *platformWindow =
410 static_cast<QAndroidPlatformWindow *>(window->handle());
411 const auto guard = platformWindow->destructionGuard();
412 if (!platformWindow->m_androidSurfaceCreated)
414 if (platformWindow->nativeViewId() == windowId)
415 platformWindow->onSurfaceChanged(surface);
420 jboolean focus, jint windowId)
426 qCWarning(lcQpaWindow,
427 "windowFocusChanged event received for non-existing window %d", windowId);
432 QWindowSystemInterface::handleFocusWindowChanged(window);
433 }
else if (!focus && window ==
qGuiApp->focusWindow()) {
435 QWindowSystemInterface::handleFocusWindowChanged(
nullptr);
440 QtJniTypes::Insets insets, jint id)
448 if (!insets.isValid())
452 for (QWindow *window :
qGuiApp->allWindows()) {
453 if (!window->handle())
455 QAndroidPlatformWindow *pw =
static_cast<QAndroidPlatformWindow *>(window->handle());
456 if (pw->nativeViewId() == id) {
465 QMargins safeMargins = QMargins(
466 insets.getField<
int>(
"left"),
467 insets.getField<
int>(
"top"),
468 insets.getField<
int>(
"right"),
469 insets.getField<
int>(
"bottom"));
471 if (safeMargins != pWindow->safeAreaMargins()) {
472 pWindow->setSafeAreaMargins(safeMargins);
473 QWindowSystemInterface::handleSafeAreaMarginsChanged(pWindow->window());
482 if (QGuiApplication::instance() !=
nullptr) {
483 const auto tlw = QGuiApplication::topLevelWindows();
484 for (QWindow *w : tlw) {
490 const QRect availableGeometry = w->screen()->availableGeometry();
491 const QRect geometry = w->geometry();
492 const bool isPositiveGeometry = (geometry.width() > 0 && geometry.height() > 0);
493 const bool isPositiveAvailableGeometry =
494 (availableGeometry.width() > 0 && availableGeometry.height() > 0);
496 if (isPositiveGeometry && isPositiveAvailableGeometry) {
497 const QRegion region = QRegion(QRect(QPoint(), w->geometry().size()));
498 QWindowSystemInterface::handleExposeEvent(w, region);
503Q_DECLARE_JNI_NATIVE_METHOD(updateWindows)
506
507
508
509
510
511
512QMutexLocker<QMutex> QAndroidPlatformWindow::destructionGuard()
514 return QMutexLocker(&m_destructionMutex);
517Q_CONSTINIT
static QBasicAtomicInt g_surfacesCounter = Q_BASIC_ATOMIC_INITIALIZER(0);
521 return g_surfacesCounter.loadRelaxed();
526 g_surfacesCounter.fetchAndAddRelaxed(1);
531 int cur = g_surfacesCounter.loadRelaxed();
536 if (g_surfacesCounter.testAndSetRelaxed(cur, cur - 1))
543 if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtWindow>::className(),
545 Q_JNI_NATIVE_METHOD(updateWindows),
546 Q_JNI_NATIVE_SCOPED_METHOD(setSurface, QAndroidPlatformWindow),
547 Q_JNI_NATIVE_SCOPED_METHOD(windowFocusChanged, QAndroidPlatformWindow),
548 Q_JNI_NATIVE_SCOPED_METHOD(safeAreaMarginsChanged, QAndroidPlatformWindow)
550 qCCritical(lcQpaWindow) <<
"RegisterNatives failed for"
551 << QtJniTypes::Traits<QtJniTypes::QtWindow>::className();
555 if (!env.registerNativeMethods(
556 QtJniTypes::Traits<QtJniTypes::QtWindowInsetsController>::className(),
558 Q_JNI_NATIVE_SCOPED_METHOD(applySystemUiNative, QAndroidPlatformWindow)
560 qCCritical(lcQpaWindow) <<
"RegisterNatives failed for"
561 << QtJniTypes::Traits<QtJniTypes::QtWindowInsetsController>::className();
AndroidBackendRegister * backendRegister()
QWindow * windowFromId(int windowId)
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")