Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qandroidplatformwindow.cpp
Go to the documentation of this file.
1// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
2// Copyright (C) 2016 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
8
10
11#include <qguiapplication.h>
12#include <qpa/qwindowsysteminterface.h>
13#include <private/qhighdpiscaling_p.h>
14#include <private/qjnihelpers_p.h>
15
17
18Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window")
19
20Q_DECLARE_JNI_CLASS(QtWindowInterface, "org/qtproject/qt/android/QtWindowInterface")
21Q_DECLARE_JNI_CLASS(QtInputInterface, "org/qtproject/qt/android/QtInputInterface")
22Q_DECLARE_JNI_CLASS(QtInputConnectionListener,
23 "org/qtproject/qt/android/QtInputConnection$QtInputConnectionListener")
24Q_DECLARE_JNI_CLASS(QtDisplayManager, "org/qtproject/qt/android/QtWindowInterface")
25Q_DECLARE_JNI_CLASS(QtWindowInsetsController, "org/qtproject/qt/android/QtWindowInsetsController")
26
27QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
28 : QPlatformWindow(window), m_nativeQtWindow(nullptr),
29 m_surfaceContainerType(SurfaceContainer::TextureView), m_nativeParentQtWindow(nullptr),
30 m_androidSurfaceObject(nullptr)
31{
32 if (window->surfaceType() == QSurface::RasterSurface)
33 window->setSurfaceType(QSurface::OpenGLSurface);
34}
35
37{
39 return;
40
41 QWindow *window = QPlatformWindow::window();
42
43 if (parent()) {
44 QAndroidPlatformWindow *androidParent = static_cast<QAndroidPlatformWindow*>(parent());
45 if (!androidParent->isEmbeddingContainer())
46 m_nativeParentQtWindow = androidParent->nativeWindow();
47 }
48
50 QtJniTypes::QtInputConnectionListener listener =
51 reg->callInterface<QtJniTypes::QtInputInterface, QtJniTypes::QtInputConnectionListener>(
52 "getInputConnectionListener");
53
54 m_nativeQtWindow = QJniObject::construct<QtJniTypes::QtWindow>(
55 QNativeInterface::QAndroidApplication::context(),
56 isForeignWindow(), m_nativeParentQtWindow, listener);
57 m_nativeViewId = m_nativeQtWindow.callMethod<jint>("getId");
58
59 // the surfaceType is overwritten in QAndroidPlatformOpenGLWindow ctor so let's save
60 // the fact that it's a raster window for now
61 m_isRaster = window->surfaceType() == QSurface::RasterSurface;
62
63 // the following is in relation to the virtual geometry
64 const bool forceMaximize = window->windowStates() & (Qt::WindowMaximized | Qt::WindowFullScreen);
65 const QRect nativeScreenGeometry = platformScreen()->availableGeometry();
66 if (forceMaximize) {
67 setGeometry(nativeScreenGeometry);
68 } else {
69 const QRect requestedNativeGeometry = QHighDpi::toNativePixels(window->geometry(), window);
70 const QRect availableDeviceIndependentGeometry = (window->parent())
71 ? window->parent()->geometry()
72 : QHighDpi::fromNativePixels(nativeScreenGeometry, window);
73 // initialGeometry returns in native pixels
74 const QRect finalNativeGeometry = QPlatformWindow::initialGeometry(
75 window, requestedNativeGeometry, availableDeviceIndependentGeometry.width(),
76 availableDeviceIndependentGeometry.height());
77 setGeometry(finalNativeGeometry);
78 }
79
80 if (window->isTopLevel())
81 platformScreen()->addWindow(this);
82
83 static bool ok = false;
84 static const int value = qEnvironmentVariableIntValue("QT_ANDROID_SURFACE_CONTAINER_TYPE", &ok);
85 if (ok) {
86 static const SurfaceContainer type = static_cast<SurfaceContainer>(value);
89 } else if (platformScreen()->windows().size() <= 1) {
90 // TODO should handle case where this changes at runtime -> need to change existing window
91 // into TextureView (or perhaps not, if the parent window would be SurfaceView, as long as
92 // onTop was false it would stay below the children)
94 }
95 qCDebug(lcQpaWindow) << "Window" << m_nativeViewId << "using surface container type"
96 << static_cast<int>(m_surfaceContainerType);
97}
98
100{
101 const auto guard = destructionGuard();
102 if (window()->isTopLevel())
103 platformScreen()->removeWindow(this);
104}
105
106
108{
109 if (m_nativeParentQtWindow.isValid()) {
110 m_nativeParentQtWindow.callMethod<void>("bringChildToBack", nativeViewId());
111 return;
112 }
113 platformScreen()->lower(this);
114}
115
117{
118 if (m_nativeParentQtWindow.isValid()) {
119 m_nativeParentQtWindow.callMethod<void>("bringChildToFront", nativeViewId());
120 QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason);
121 return;
122 }
123 updateSystemUiVisibility(window()->windowStates(), window()->flags());
124 platformScreen()->raise(this);
125}
126
128{
129 return m_safeAreaMargins;
130}
131
132void QAndroidPlatformWindow::setSafeAreaMargins(const QMargins safeMargins)
133{
134 m_safeAreaMargins = safeMargins;
135}
136
137void QAndroidPlatformWindow::setGeometry(const QRect &rect)
138{
140 Q_ASSERT(m_nativeQtWindow.isValid());
141
142 jint x = 0;
143 jint y = 0;
144 jint w = -1;
145 jint h = -1;
146 if (!rect.isNull()) {
147 x = rect.x();
148 y = rect.y();
149 w = rect.width();
150 h = rect.height();
151 }
152 m_nativeQtWindow.callMethod<void>("setGeometry", x, y, w, h);
153 }
154
155 QWindowSystemInterface::handleGeometryChange(window(), rect);
156}
157
159{
161 return;
162
163 if (window()->isTopLevel()) {
164 if (!visible && window() == qGuiApp->focusWindow()) {
165 platformScreen()->topVisibleWindowChanged();
166 } else {
167 const Qt::WindowStates states = window()->windowStates();
168 const Qt::WindowFlags flags = window()->flags();
169 updateSystemUiVisibility(states, flags);
170 if (states & Qt::WindowFullScreen || flags & Qt::ExpandedClientAreaHint)
171 setGeometry(platformScreen()->geometry());
172 else if (states & Qt::WindowMaximized)
173 setGeometry(platformScreen()->availableGeometry());
175 }
176 }
177
178 m_nativeQtWindow.callMethod<void>("setVisible", visible);
179
180 if (geometry().isEmpty() || screen()->availableGeometry().isEmpty())
181 return;
182
183 QPlatformWindow::setVisible(visible);
184}
185
186void QAndroidPlatformWindow::setWindowState(Qt::WindowStates state)
187{
188 QPlatformWindow::setWindowState(state);
189
190 if (window()->isVisible())
191 updateSystemUiVisibility(state, window()->flags());
192}
193
194void QAndroidPlatformWindow::setWindowFlags(Qt::WindowFlags flags)
195{
196 QPlatformWindow::setWindowFlags(flags);
197
198 if (window()->isVisible())
199 updateSystemUiVisibility(window()->windowStates(), flags);
200}
201
202void QAndroidPlatformWindow::setParent(const QPlatformWindow *window)
203{
204 using namespace QtJniTypes;
205
206 if (window) {
207 auto androidWindow = static_cast<const QAndroidPlatformWindow*>(window);
208 if (androidWindow->isEmbeddingContainer())
209 return;
210 // If we were a top level window, remove from screen
211 if (!m_nativeParentQtWindow.isValid())
212 platformScreen()->removeWindow(this);
213
214 const QtWindow parentWindow = androidWindow->nativeWindow();
215 // If this was a child window of another window, the java method takes care of that
216 m_nativeQtWindow.callMethod<void, QtWindow>("setParent", parentWindow.object());
217 m_nativeParentQtWindow = parentWindow;
218 } else if (QtAndroid::isQtApplication()) {
219 m_nativeQtWindow.callMethod<void, QtWindow>("setParent", nullptr);
220 m_nativeParentQtWindow = QJniObject();
221 platformScreen()->addWindow(this);
222 }
223}
224
226{
227 return m_nativeQtWindow.isValid() ? reinterpret_cast<WId>(m_nativeQtWindow.object()) : 0L;
228}
229
231{
232 return static_cast<QAndroidPlatformScreen *>(window()->screen()->handle());
233}
234
236{
237 //shut up warning from default implementation
238}
239
241{
242 // raise() will handle differences between top level and child windows, and requesting focus
243 if (!blockedByModal())
244 raise();
245}
246
247void QAndroidPlatformWindow::updateSystemUiVisibility(Qt::WindowStates states, Qt::WindowFlags flags)
248{
249 const bool isNonRegularWindow = flags & (Qt::Popup | Qt::Dialog | Qt::Sheet) & ~Qt::Window;
250 if (!isNonRegularWindow) {
251 auto iface = qGuiApp->nativeInterface<QNativeInterface::QAndroidApplication>();
252 iface->runOnAndroidMainThread([=]() {
253 using namespace QtJniTypes;
254 auto activity = iface->context().object<Activity>();
255 if (states & Qt::WindowFullScreen)
256 QtWindowInsetsController::callStaticMethod("showFullScreen", activity);
257 else if (flags & Qt::ExpandedClientAreaHint)
258 QtWindowInsetsController::callStaticMethod("showExpanded", activity);
259 else
260 QtWindowInsetsController::callStaticMethod("showNormal", activity);
261 });
262 }
263}
264
266{
267 m_nativeQtWindow.callMethod<void>("updateFocusedEditText");
268}
269
271{
272 return qApp->applicationState() > Qt::ApplicationHidden
273 && window()->isVisible()
274 && !window()->geometry().isEmpty();
275}
276
278{
279 QRegion region;
280 if (isExposed())
281 region = QRect(QPoint(), geometry().size());
282
283 QWindowSystemInterface::handleExposeEvent(window(), region);
284 QWindowSystemInterface::flushWindowSystemEvents();
285}
286
288{
289 const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
290 const bool isOpaque = !format().hasAlpha() && qFuzzyCompare(window()->opacity(), qreal(1.0));
291
292 m_nativeQtWindow.callMethod<void>("createSurface", windowStaysOnTop, 32, isOpaque,
293 m_surfaceContainerType);
295}
296
298{
300 m_nativeQtWindow.callMethod<void>("destroySurface");
302 }
303}
304
305void QAndroidPlatformWindow::onSurfaceChanged(QtJniTypes::Surface surface)
306{
308 const bool surfaceIsValid = surface.isValid();
309 qCDebug(lcQpaWindow) << "onSurfaceChanged(): valid Surface received" << surfaceIsValid;
310 m_androidSurfaceObject = surface;
311 if (surfaceIsValid) {
312 // wait until we have a valid surface to draw into
313 m_surfaceWaitCondition.wakeOne();
314 } else {
316 }
317
319
321}
322
324{
325 QRect availableGeometry = screen()->availableGeometry();
326 if (!geometry().isNull() && !availableGeometry.isNull()) {
327 QWindowSystemInterface::handleExposeEvent(window(),
328 QRegion(QRect(QPoint(), geometry().size())));
329 }
330}
331
333{
334 QWindow *modalWindow = QGuiApplication::modalWindow();
335 return modalWindow && modalWindow != window();
336}
337
339{
340 // Returns true if the window is a wrapper for a foreign window solely to allow embedding Qt
341 // into a native Android app, in which case we should not try to control it more than we "need" to
342 return !QtAndroid::isQtApplication() && window()->isTopLevel();
343}
344
345void QAndroidPlatformWindow::setSurface(JNIEnv *env, jobject object, jint windowId,
346 QtJniTypes::Surface surface)
347{
348 Q_UNUSED(env)
349 Q_UNUSED(object)
350
351 if (!qGuiApp)
352 return;
353
354 const QList<QWindow*> windows = qGuiApp->allWindows();
355 for (QWindow * window : windows) {
356 if (!window->handle())
357 continue;
358 QAndroidPlatformWindow *platformWindow =
359 static_cast<QAndroidPlatformWindow *>(window->handle());
360 const auto guard = platformWindow->destructionGuard();
361 if (!platformWindow->m_androidSurfaceCreated)
362 continue;
363 if (platformWindow->nativeViewId() == windowId)
364 platformWindow->onSurfaceChanged(surface);
365 }
366}
367
368void QAndroidPlatformWindow::windowFocusChanged(JNIEnv *env, jobject object,
369 jboolean focus, jint windowId)
370{
371 Q_UNUSED(env)
372 Q_UNUSED(object)
373 QWindow* window = QtAndroid::windowFromId(windowId);
374 if (!window) {
375 qCWarning(lcQpaWindow,
376 "windowFocusChanged event received for non-existing window %d", windowId);
377 return;
378 }
379
380 if (focus) {
381 QWindowSystemInterface::handleFocusWindowChanged(window);
382 } else if (!focus && window == qGuiApp->focusWindow()) {
383 // Clear focus if current window has lost focus
384 QWindowSystemInterface::handleFocusWindowChanged(nullptr);
385 }
386}
387
388void QAndroidPlatformWindow::safeAreaMarginsChanged(JNIEnv *env, jobject object,
389 QtJniTypes::Insets insets, jint id)
390{
391 Q_UNUSED(env)
392 Q_UNUSED(object)
393
394 if (!qGuiApp)
395 return;
396
397 if (!insets.isValid())
398 return;
399
400 QAndroidPlatformWindow *pWindow = nullptr;
401 for (QWindow *window : qGuiApp->allWindows()) {
402 if (!window->handle())
403 continue;
404 QAndroidPlatformWindow *pw = static_cast<QAndroidPlatformWindow *>(window->handle());
405 if (pw->nativeViewId() == id) {
406 pWindow = pw;
407 break;
408 }
409 }
410
411 if (!pWindow)
412 return;
413
414 QMargins safeMargins = QMargins(
415 insets.getField<int>("left"),
416 insets.getField<int>("top"),
417 insets.getField<int>("right"),
418 insets.getField<int>("bottom"));
419
420 if (safeMargins != pWindow->safeAreaMargins()) {
421 pWindow->setSafeAreaMargins(safeMargins);
422 QWindowSystemInterface::handleSafeAreaMarginsChanged(pWindow->window());
423 }
424}
425
426static void updateWindows(JNIEnv *env, jobject object)
427{
428 Q_UNUSED(env)
429 Q_UNUSED(object)
430
431 if (QGuiApplication::instance() != nullptr) {
432 const auto tlw = QGuiApplication::topLevelWindows();
433 for (QWindow *w : tlw) {
434
435 // Skip non-platform windows, e.g., offscreen windows.
436 if (!w->handle())
437 continue;
438
439 const QRect availableGeometry = w->screen()->availableGeometry();
440 const QRect geometry = w->geometry();
441 const bool isPositiveGeometry = (geometry.width() > 0 && geometry.height() > 0);
442 const bool isPositiveAvailableGeometry =
443 (availableGeometry.width() > 0 && availableGeometry.height() > 0);
444
445 if (isPositiveGeometry && isPositiveAvailableGeometry) {
446 const QRegion region = QRegion(QRect(QPoint(), w->geometry().size()));
447 QWindowSystemInterface::handleExposeEvent(w, region);
448 }
449 }
450 }
451}
452Q_DECLARE_JNI_NATIVE_METHOD(updateWindows)
453
454/*
455 Due to calls originating from Android, it is possible for native methods to
456 try to manipulate any given instance of QAndroidPlatformWindow when it is
457 already being destroyed. So we use this to guard against that. It is called
458 in the destructor, and should also be called in any function registered to
459 be called from java that may touch an instance of QAndroidPlatformWindow.
460 */
461QMutexLocker<QMutex> QAndroidPlatformWindow::destructionGuard()
462{
463 return QMutexLocker(&m_destructionMutex);
464}
465
466bool QAndroidPlatformWindow::registerNatives(QJniEnvironment &env)
467{
468 if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtWindow>::className(),
469 {
470 Q_JNI_NATIVE_METHOD(updateWindows),
471 Q_JNI_NATIVE_SCOPED_METHOD(setSurface, QAndroidPlatformWindow),
472 Q_JNI_NATIVE_SCOPED_METHOD(windowFocusChanged, QAndroidPlatformWindow),
473 Q_JNI_NATIVE_SCOPED_METHOD(safeAreaMarginsChanged, QAndroidPlatformWindow)
474 })) {
475 qCCritical(lcQpaWindow) << "RegisterNatives failed for"
476 << QtJniTypes::Traits<QtJniTypes::QtWindow>::className();
477 return false;
478 }
479 return true;
480}
481
482QT_END_NAMESPACE
void propagateSizeHints() override
Reimplement to propagate the size hints of the QWindow.
virtual void applicationStateChanged(Qt::ApplicationState)
void setSafeAreaMargins(const QMargins safeMargins)
void setWindowFlags(Qt::WindowFlags flags) override
Requests setting the window flags of this surface to flags.
void setWindowState(Qt::WindowStates state) override
Requests setting the window state of this surface to type.
void onSurfaceChanged(QtJniTypes::Surface surface)
bool isExposed() const override
Returns if this window is exposed in the windowing system.
SurfaceContainer m_surfaceContainerType
QAndroidPlatformScreen * platformScreen() const
void requestActivateWindow() override
Reimplement to let Qt be able to request activation/focus for a window.
void updateSystemUiVisibility(Qt::WindowStates states, Qt::WindowFlags flags)
void initialize() override
Called as part of QWindow::create(), after constructing the window.
void setVisible(bool visible) override
Reimplemented in subclasses to show the surface if visible is true, and hide it if visible is false.
void setParent(const QPlatformWindow *window) override
This function is called to enable native child window in QPA.
void lower() override
Reimplement to be able to let Qt lower windows to the bottom of the desktop.
WId winId() const override
Reimplement in subclasses to return a handle to the native window.
void raise() override
Reimplement to be able to let Qt raise windows to the top of the desktop.
void setGeometry(const QRect &rect) override
This function is called by Qt whenever a window is moved or resized using the QWindow API.
QMargins safeAreaMargins() const override
The safe area margins of a window represent the area that is safe to place content within,...
QObject * parent
Definition qobject.h:73
Combined button and popup list for selecting options.
AndroidBackendRegister * backendRegister()
QWindow * windowFromId(int windowId)
bool isQtApplication()
static void updateWindows(JNIEnv *env, jobject object)
#define qApp
#define qGuiApp