13#include <qguiapplication.h>
14#include <qpa/qwindowsysteminterface.h>
16#include <private/qhighdpiscaling_p.h>
17#include <private/qjnihelpers_p.h>
19#include <android/native_window.h>
20#include <android/native_window_jni.h>
29Q_DECLARE_JNI_CLASS(QtWindowInterface,
"org/qtproject/qt/android/QtWindowInterface")
30Q_DECLARE_JNI_CLASS(QtInputInterface,
"org/qtproject/qt/android/QtInputInterface")
31Q_DECLARE_JNI_CLASS(QtInputConnectionListener,
32 "org/qtproject/qt/android/QtInputConnection$QtInputConnectionListener")
33Q_DECLARE_JNI_CLASS(QtDisplayManager,
"org/qtproject/qt/android/QtWindowInterface")
34Q_DECLARE_JNI_CLASS(QtWindowInsetsController,
"org/qtproject/qt/android/QtWindowInsetsController")
36QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
37 : QPlatformWindow(window), m_nativeQtWindow(
nullptr),
38 m_surfaceContainerType(SurfaceContainer::TextureView), m_nativeParentQtWindow(
nullptr)
40 if (window->surfaceType() == QSurface::RasterSurface)
41 window->setSurfaceType(QSurface::OpenGLSurface);
49 QWindow *window = QPlatformWindow::window();
53 if (!androidParent->isEmbeddingContainer())
54 m_nativeParentQtWindow = androidParent->nativeWindow();
58 QtJniTypes::QtInputConnectionListener listener =
59 reg->callInterface<QtJniTypes::QtInputInterface, QtJniTypes::QtInputConnectionListener>(
60 "getInputConnectionListener");
62 m_nativeQtWindow = QJniObject::construct<QtJniTypes::QtWindow>(
63 QNativeInterface::QAndroidApplication::context(),
64 isForeignWindow(), m_nativeParentQtWindow, listener);
69 m_isRaster = window->surfaceType() == QSurface::RasterSurface;
72 const bool forceMaximize = window->windowStates() & (Qt::WindowMaximized | Qt::WindowFullScreen);
73 const QRect nativeScreenGeometry = platformScreen()->availableGeometry();
75 setGeometry(nativeScreenGeometry);
77 const QRect requestedNativeGeometry = QHighDpi::toNativePixels(window->geometry(), window);
78 const QRect availableDeviceIndependentGeometry = (window->parent())
79 ? window->parent()->geometry()
80 : QHighDpi::fromNativePixels(nativeScreenGeometry, window);
82 const QRect finalNativeGeometry = QPlatformWindow::initialGeometry(
83 window, requestedNativeGeometry, availableDeviceIndependentGeometry.width(),
84 availableDeviceIndependentGeometry.height());
85 setGeometry(finalNativeGeometry);
88 if (window->isTopLevel())
89 platformScreen()->addWindow(
this);
91 static bool ok =
false;
92 static const int value = qEnvironmentVariableIntValue(
"QT_ANDROID_SURFACE_CONTAINER_TYPE", &ok);
97 }
else if (platformScreen()->windows().size() <= 1) {
103 qCDebug(lcQpaWindow) <<
"Window" <<
m_nativeViewId <<
"using surface container type"
110 if (window()->isTopLevel())
111 platformScreen()->removeWindow(
this);
116 if (m_nativeParentQtWindow.isValid()) {
117 m_nativeParentQtWindow.callMethod<
void>(
"bringChildToBack", nativeViewId());
120 platformScreen()->lower(
this);
125 if (m_nativeParentQtWindow.isValid()) {
126 m_nativeParentQtWindow.callMethod<
void>(
"bringChildToFront", nativeViewId());
129 applySystemUi(window()->windowStates(), window()->flags());
130 platformScreen()->raise(
this);
135 return m_safeAreaMargins;
140 m_safeAreaMargins = safeMargins;
145 QWindowSystemInterface::handleGeometryChange(window(), rect);
154 const QWindow *parent = window()->parent();
155 return parent && parent->handle()
164 const QMargins parentMargins = parent->safeAreaMargins();
165 const QSize parentSize = parent->geometry().size();
166 const QRect rect = geometry();
167 const QMargins margins(
168 qBound(0, parentMargins.left() - rect.x(), parentMargins.left()),
169 qBound(0, parentMargins.top() - rect.y(), parentMargins.top()),
170 qBound(0, parentMargins.right() - (parentSize.width() - rect.x() - rect.width()),
171 parentMargins.right()),
172 qBound(0, parentMargins.bottom() - (parentSize.height() - rect.y() - rect.height()),
173 parentMargins.bottom()));
174 if (margins != m_safeAreaMargins) {
175 m_safeAreaMargins = margins;
179 for (QObject *object : window()->children()) {
180 auto *child = qobject_cast<QWindow *>(object);
181 if (child && child->handle())
182 static_cast<QAndroidPlatformWindow *>(child->handle())->updateDerivedSafeAreaMargins();
192 const bool fillsLayout = window()->isTopLevel()
193 && window()->windowStates() & (Qt::WindowMaximized | Qt::WindowFullScreen);
194 const QRect rect = fillsLayout ? QRect() : geometry();
199 if (!rect.isNull()) {
205 m_nativeQtWindow.callMethod<
void>(
"updateWindow", x, y, w, h, visible);
213 if (window()->isTopLevel()) {
214 if (!visible && window() ==
qGuiApp->focusWindow()) {
215 platformScreen()->topVisibleWindowChanged();
217 const Qt::WindowStates states = window()->windowStates();
218 const Qt::WindowFlags flags = window()->flags();
219 applySystemUi(states, flags);
220 if (states & Qt::WindowFullScreen
221 || ((states & Qt::WindowMaximized) && (flags & Qt::ExpandedClientAreaHint)))
222 setGeometry(platformScreen()->geometry());
223 else if (states & Qt::WindowMaximized)
224 setGeometry(platformScreen()->availableGeometry());
230 const bool onTop = window()->flags() & Qt::WindowStaysOnTopHint;
231 const bool isOpaque = !format().hasAlpha()
232 && qFuzzyCompare(window()->opacity(), qreal(1.0));
233 m_nativeQtWindow.callMethod<
void>(
"configureSurface", onTop, isOpaque,
234 m_surfaceContainerType);
238 if (geometry().isEmpty() || screen()->availableGeometry().isEmpty())
241 QPlatformWindow::setVisible(visible);
247 QPlatformWindow::setWindowState(state);
249 if (window()->isVisible())
250 applySystemUi(state, window()->flags());
257 QPlatformWindow::setWindowFlags(flags);
259 if (window()->isVisible())
260 applySystemUi(window()->windowStates(), flags);
265 using namespace QtJniTypes;
269 if (androidWindow->isEmbeddingContainer())
272 if (!m_nativeParentQtWindow.isValid())
273 platformScreen()->removeWindow(
this);
275 const QtWindow parentWindow = androidWindow->nativeWindow();
277 m_nativeQtWindow.callMethod<
void, QtWindow>(
"setParent", parentWindow.object());
278 m_nativeParentQtWindow = parentWindow;
280 m_nativeQtWindow.callMethod<
void, QtWindow>(
"setParent",
nullptr);
281 m_nativeParentQtWindow = QJniObject();
282 platformScreen()->addWindow(
this);
289 return m_nativeQtWindow.isValid() ?
reinterpret_cast<WId>(m_nativeQtWindow.object()) : 0L;
308 if (m_nativeParentQtWindow.isValid())
309 QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason);
316enum class SystemUiMode { Expanded, FullScreen };
318std::optional<SystemUiMode> systemUiModeFor(Qt::WindowStates states, Qt::WindowFlags flags)
320 if (states & Qt::WindowFullScreen)
321 return SystemUiMode::FullScreen;
322 if (flags & Qt::ExpandedClientAreaHint)
323 return SystemUiMode::Expanded;
329std::optional<SystemUiMode> g_ownedSystemUiMode;
331void applySystemUiMode(
std::optional<SystemUiMode> mode)
333 g_ownedSystemUiMode = mode;
335 auto iface =
qGuiApp->nativeInterface<QNativeInterface::QAndroidApplication>();
336 iface->runOnAndroidMainThread([mode, iface]() {
337 using namespace QtJniTypes;
338 auto activity = iface->context().object<Activity>();
339 if (mode == SystemUiMode::FullScreen)
340 QtWindowInsetsController::callStaticMethod(
"showFullScreen", activity);
341 else if (mode == SystemUiMode::Expanded)
342 QtWindowInsetsController::callStaticMethod(
"showExpanded", activity);
344 QtWindowInsetsController::callStaticMethod(
"showNormal", activity);
351 const bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window;
352 if (isNonRegularWindow)
355 const std::optional<SystemUiMode> mode = systemUiModeFor(states, flags);
356 if (mode != g_ownedSystemUiMode)
357 applySystemUiMode(mode);
370 QMetaObject::invokeMethod(
qGuiApp, [] {
371 if (g_ownedSystemUiMode)
372 applySystemUiMode(g_ownedSystemUiMode);
378 m_nativeQtWindow.callMethod<
void>(
"updateFocusedEditText");
385 const QWindow *parent = window()->parent();
386 const auto state =
qApp->applicationState();
387 return m_surfaceValid
388 && (!parent || !parent->handle() || parent->handle()->isExposed())
389 && (state == Qt::ApplicationInactive || state == Qt::ApplicationActive)
390 && window()->isVisible()
391 && !window()->geometry().isEmpty();
398 region = QRect(QPoint(), geometry().size());
400 QWindowSystemInterface::handleExposeEvent(window(), region);
402 QWindowSystemInterface::flushWindowSystemEvents();
408 for (QObject *object : window()->children()) {
409 auto *child = qobject_cast<QWindow *>(object);
410 if (!child || !child->handle())
412 auto *platformChild =
static_cast<QAndroidPlatformWindow *>(child->handle());
414 if (platformChild->isExposed())
415 region = QRect(QPoint(), platformChild->geometry().size());
416 QWindowSystemInterface::handleExposeEvent(child, region);
417 platformChild->sendChildExposeEvents();
423 if (m_nativeWindow || QThread::currentThread() !=
qApp->thread())
425 if (!m_nativeQtWindow.isValid())
429 auto surface = m_nativeQtWindow.callMethod<QtJniTypes::Surface>(
"getSurface");
430 if (surface.isValid() && surface.callMethod<jboolean>(
"isValid")) {
431 if (ANativeWindow *native = ANativeWindow_fromSurface(
432 QJniEnvironment::getJniEnv(), surface.object())) {
440 qCDebug(lcQpaWindow) <<
"onSurfaceCreated() for windowId" <<
m_nativeViewId;
444 ANativeWindow *native = ANativeWindow_fromSurface(QJniEnvironment::getJniEnv(),
447 qCWarning(lcQpaWindow) <<
"ANativeWindow_fromSurface returned null for windowId"
455 ANativeWindow_release(native);
460 QPlatformSurfaceEvent surfaceEvent(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
461 QGuiApplication::sendEvent(window(), &surfaceEvent);
468 QPlatformSurfaceEvent surfaceEvent(QPlatformSurfaceEvent::SurfaceCreated);
469 QGuiApplication::sendEvent(window(), &surfaceEvent);
476 QWindowSystemInterface::handleExposeEvent(window(),
477 QRegion(QRect(QPoint(), geometry().size())));
483 qCDebug(lcQpaWindow) <<
"onRedrawNeeded() for windowId" <<
m_nativeViewId;
486 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(
487 window(), QRegion(QRect(QPoint(), geometry().size())));
493 qCDebug(lcQpaWindow) <<
"onSurfaceDestroyed() for windowId" <<
m_nativeViewId;
500 QWindowSystemInterface::handleExposeEvent<QWindowSystemInterface::SynchronousDelivery>(
501 window(), QRegion());
506 QPlatformSurfaceEvent surfaceEvent(QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed);
507 QGuiApplication::sendEvent(window(), &surfaceEvent);
526 if (m_nativeQtWindow.isValid())
527 m_nativeQtWindow.callMethod<
void>(
"destroySurface");
532 QWindow *modalWindow = QGuiApplication::modalWindow();
533 return modalWindow && modalWindow != window();
545 const QList<QWindow*> windows =
qGuiApp->allWindows();
546 for (QWindow *w : windows) {
549 auto *pw =
static_cast<QAndroidPlatformWindow *>(w->handle());
550 if (pw->nativeViewId() == windowId)
557 QtJniTypes::Surface surface)
565 QJniObject keepalive(surface);
566 QMetaObject::invokeMethod(
qApp, [windowId, keepalive]() {
567 if (
auto *pw = findWindowById(windowId))
568 pw->onSurfaceCreated(QtJniTypes::Surface(keepalive));
569 }, Qt::QueuedConnection);
573 QtJniTypes::Runnable drawingFinished)
577 QJniObject finished(drawingFinished);
579 if (!
qGuiApp || (QtAndroid::blockEventLoopsWhenSuspended()
580 && QAndroidEventDispatcherStopper::stopped())) {
581 finished.callMethod<
void>(
"run");
586 QMetaObject::invokeMethod(
qApp, [windowId, finished]() {
587 if (
auto *pw = findWindowById(windowId))
588 pw->onRedrawNeeded();
589 finished.callMethod<
void>(
"run");
590 }, Qt::QueuedConnection);
594 QtJniTypes::Runnable surfaceReleased)
598 QJniObject released(surfaceReleased);
599 const auto finish = [released] {
600 if (released.isValid())
601 released.callMethod<
void>(
"run");
608 QMetaObject::invokeMethod(
qApp, [windowId, finish]() {
609 if (
auto *pw = findWindowById(windowId))
610 pw->onSurfaceDestroyed();
612 }, Qt::QueuedConnection);
616 jboolean focus, jint windowId)
624 QMetaObject::invokeMethod(
qApp, [windowId, focus]() {
625 QWindow *window = QtAndroid::windowFromId(windowId);
627 qCWarning(lcQpaWindow,
628 "windowFocusChanged event received for non-existing window %d", windowId);
632 QWindowSystemInterface::handleFocusWindowChanged(window);
633 else if (window ==
qGuiApp->focusWindow())
634 QWindowSystemInterface::handleFocusWindowChanged(
nullptr);
635 }, Qt::QueuedConnection);
639 QtJniTypes::Insets insets, jint id)
647 if (!insets.isValid())
650 const QMargins safeMargins(insets.getField<
int>(
"left"),
651 insets.getField<
int>(
"top"),
652 insets.getField<
int>(
"right"),
653 insets.getField<
int>(
"bottom"));
656 QMetaObject::invokeMethod(
qApp, [id, safeMargins]() {
657 QAndroidPlatformWindow *pw = findWindowById(id);
659 if (!pw || pw->isChildOfQtWindow())
661 if (safeMargins != pw->safeAreaMargins()) {
662 pw->setSafeAreaMargins(safeMargins);
663 QWindowSystemInterface::handleSafeAreaMarginsChanged(pw->window());
665 pw->updateDerivedSafeAreaMargins();
666 }, Qt::QueuedConnection);
674 if (QGuiApplication::instance() !=
nullptr) {
675 const auto tlw = QGuiApplication::topLevelWindows();
676 for (QWindow *w : tlw) {
682 const QRect availableGeometry = w->screen()->availableGeometry();
683 const QRect geometry = w->geometry();
684 const bool isPositiveGeometry = (geometry.width() > 0 && geometry.height() > 0);
685 const bool isPositiveAvailableGeometry =
686 (availableGeometry.width() > 0 && availableGeometry.height() > 0);
688 if (isPositiveGeometry && isPositiveAvailableGeometry) {
689 const QRegion region = QRegion(QRect(QPoint(), w->geometry().size()));
690 QWindowSystemInterface::handleExposeEvent(w, region);
695Q_DECLARE_JNI_NATIVE_METHOD(updateWindows)
699 if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtWindow>::className(),
701 Q_JNI_NATIVE_METHOD(updateWindows),
702 Q_JNI_NATIVE_SCOPED_METHOD(surfaceCreated, QAndroidPlatformWindow),
703 Q_JNI_NATIVE_SCOPED_METHOD(surfaceRedrawNeeded, QAndroidPlatformWindow),
704 Q_JNI_NATIVE_SCOPED_METHOD(surfaceDestroyed, QAndroidPlatformWindow),
705 Q_JNI_NATIVE_SCOPED_METHOD(windowFocusChanged, QAndroidPlatformWindow),
706 Q_JNI_NATIVE_SCOPED_METHOD(safeAreaMarginsChanged, QAndroidPlatformWindow)
708 qCCritical(lcQpaWindow) <<
"RegisterNatives failed for"
709 << QtJniTypes::Traits<QtJniTypes::QtWindow>::className();
713 if (!env.registerNativeMethods(
714 QtJniTypes::Traits<QtJniTypes::QtWindowInsetsController>::className(),
716 Q_JNI_NATIVE_SCOPED_METHOD(applySystemUiNative, QAndroidPlatformWindow)
718 qCCritical(lcQpaWindow) <<
"RegisterNatives failed for"
719 << QtJniTypes::Traits<QtJniTypes::QtWindowInsetsController>::className();
QRect window() const
Returns the window rectangle.
AndroidBackendRegister * backendRegister()
Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher")