From 6ff88f97a6d24d1098583421161f8f903f9dafde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tinja=20Paavosepp=C3=A4?= Date: Tue, 19 Sep 2023 14:55:12 +0300 Subject: [PATCH] Android: Add preliminary support for child windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the manual test case for embedded windows to have native window on Android. There are still some sharp corners, for example: * The windows are implemented with SurfaceViews, which makes z-ordering with multiple of them a bit tricky. The Surfaces they instantiate are basically z-ordered to either be below everything, with a hole punched in the window, or on top of everything, with the Surfaces created later on top of the ones created earlier. Also, with the foreign views it looks like the native view is on top of the Surface, because it is created later. And since the child windows create their Surfaces before the parent, they would be behind the parent window, currently circumventing this with letting the parent be z-ordered behind everything, and the children on top of everything. A follow up commit addresses this by changing the native view class to TextureView when multiple windows are present. * Parent window always gets the touch events - fixed in a follow up commit * If a child window has a text edit, it does not receive focus when clicking on it Task-number: QTBUG-116187 Change-Id: I32188ec5e3d3fce9fd8e3a931e317d1e081f691c Reviewed-by: Assam Boudjelthia Reviewed-by: Tor Arne Vestbø --- src/android/jar/CMakeLists.txt | 1 + .../qt/android/QtAccessibilityDelegate.java | 3 +- .../qt/android/QtActivityDelegate.java | 5 +- .../org/qtproject/qt/android/QtLayout.java | 50 +++----- .../qtproject/qt/android/QtRootLayout.java | 74 +++++++++++ .../org/qtproject/qt/android/QtWindow.java | 115 +++++++++++++++--- .../android/qandroidplatformforeignwindow.cpp | 7 +- .../android/qandroidplatformforeignwindow.h | 1 - .../android/qandroidplatformopenglwindow.cpp | 6 +- .../android/qandroidplatformscreen.h | 1 - .../android/qandroidplatformvulkanwindow.cpp | 3 +- .../android/qandroidplatformwindow.cpp | 71 ++++++++--- .../android/qandroidplatformwindow.h | 3 +- tests/manual/embeddedwindows/main.cpp | 2 +- tests/shared/nativewindow.h | 50 ++++++++ 15 files changed, 305 insertions(+), 87 deletions(-) create mode 100644 src/android/jar/src/org/qtproject/qt/android/QtRootLayout.java diff --git a/src/android/jar/CMakeLists.txt b/src/android/jar/CMakeLists.txt index 82460b553c7..a4463291a9b 100644 --- a/src/android/jar/CMakeLists.txt +++ b/src/android/jar/CMakeLists.txt @@ -29,6 +29,7 @@ set(java_sources src/org/qtproject/qt/android/QtClipboardManager.java src/org/qtproject/qt/android/QtDisplayManager.java src/org/qtproject/qt/android/UsedFromNativeCode.java + src/org/qtproject/qt/android/QtRootLayout.java src/org/qtproject/qt/android/QtWindow.java ) diff --git a/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java index 9eaefed2a7c..991fb364916 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtAccessibilityDelegate.java @@ -59,7 +59,8 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate return dispatchHoverEvent(event); } } - + // TODO do we want to have one QtAccessibilityDelegate for the whole app (QtRootLayout) or + // e.g. one per window? public QtAccessibilityDelegate(QtLayout layout) { m_layout = layout; diff --git a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java index 8f98dfa28fe..b7d1f207daf 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtActivityDelegate.java @@ -37,13 +37,12 @@ class QtActivityDelegate { private Activity m_activity; - private QtLayout m_layout = null; + private QtRootLayout m_layout = null; private HashMap m_topLevelWindows; private ImageView m_splashScreen = null; private boolean m_splashScreenSticky = false; private View m_dummyView = null; - private QtAccessibilityDelegate m_accessibilityDelegate = null; private QtDisplayManager m_displayManager = null; @@ -135,7 +134,7 @@ class QtActivityDelegate private void initMembers() { - m_layout = new QtLayout(m_activity); + m_layout = new QtRootLayout(m_activity); m_membersInitialized = true; m_topLevelWindows = new HashMap(); diff --git a/src/android/jar/src/org/qtproject/qt/android/QtLayout.java b/src/android/jar/src/org/qtproject/qt/android/QtLayout.java index 2643a9afcf7..5f560db7a50 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtLayout.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtLayout.java @@ -30,37 +30,6 @@ class QtLayout extends ViewGroup super(context, attrs, defStyle); } - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) - { - Activity activity = (Activity)getContext(); - if (activity == null) - return; - - DisplayMetrics realMetrics = new DisplayMetrics(); - Display display = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) - ? activity.getWindowManager().getDefaultDisplay() - : activity.getDisplay(); - - if (display == null) - return; - - display.getRealMetrics(realMetrics); - if ((realMetrics.widthPixels > realMetrics.heightPixels) != (w > h)) { - // This is an intermediate state during display rotation. - // The new size is still reported for old orientation, while - // realMetrics contain sizes for new orientation. Setting - // such parameters will produce inconsistent results, so - // we just skip them. - // We will have another onSizeChanged() with normal values - // a bit later. - return; - } - - QtDisplayManager.setApplicationDisplayMetrics(activity, w, h); - QtDisplayManager.handleOrientationChanges(activity, true); - } - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { @@ -79,11 +48,15 @@ class QtLayout extends ViewGroup int childRight; int childBottom; - QtLayout.LayoutParams lp - = (QtLayout.LayoutParams) child.getLayoutParams(); - - childRight = lp.x + child.getMeasuredWidth(); - childBottom = lp.y + child.getMeasuredHeight(); + if (child.getLayoutParams() instanceof QtLayout.LayoutParams) { + QtLayout.LayoutParams lp + = (QtLayout.LayoutParams) child.getLayoutParams(); + childRight = lp.x + child.getMeasuredWidth(); + childBottom = lp.y + child.getMeasuredHeight(); + } else { + childRight = child.getMeasuredWidth(); + childBottom = child.getMeasuredHeight(); + } maxWidth = Math.max(maxWidth, childRight); maxHeight = Math.max(maxHeight, childBottom); @@ -181,6 +154,11 @@ class QtLayout extends ViewGroup this.y = y; } + public LayoutParams(int width, int height) + { + super(width, height); + } + /** * {@inheritDoc} */ diff --git a/src/android/jar/src/org/qtproject/qt/android/QtRootLayout.java b/src/android/jar/src/org/qtproject/qt/android/QtRootLayout.java new file mode 100644 index 00000000000..54f4ea1ee08 --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt/android/QtRootLayout.java @@ -0,0 +1,74 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2012 BogDan Vatra +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +package org.qtproject.qt.android; + +import android.app.Activity; +import android.content.Context; +import android.os.Build; +import android.util.DisplayMetrics; +import android.view.Display; + +/** + A layout which corresponds to one Activity, i.e. is the root layout where the top level window + and handles orientation changes. +*/ +public class QtRootLayout extends QtLayout +{ + + private int m_activityDisplayRotation = -1; + private int m_ownDisplayRotation = -1; + private int m_nativeOrientation = -1; + + public QtRootLayout(Context context) + { + super(context); + } + + public void setActivityDisplayRotation(int rotation) + { + m_activityDisplayRotation = rotation; + } + + public void setNativeOrientation(int orientation) + { + m_nativeOrientation = orientation; + } + + public int displayRotation() + { + return m_ownDisplayRotation; + } + + @Override + protected void onSizeChanged (int w, int h, int oldw, int oldh) + { + Activity activity = (Activity)getContext(); + if (activity == null) + return; + + DisplayMetrics realMetrics = new DisplayMetrics(); + Display display = (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) + ? activity.getWindowManager().getDefaultDisplay() + : activity.getDisplay(); + + if (display == null) + return; + + display.getRealMetrics(realMetrics); + if ((realMetrics.widthPixels > realMetrics.heightPixels) != (w > h)) { + // This is an intermediate state during display rotation. + // The new size is still reported for old orientation, while + // realMetrics contain sizes for new orientation. Setting + // such parameters will produce inconsistent results, so + // we just skip them. + // We will have another onSizeChanged() with normal values + // a bit later. + return; + } + + QtDisplayManager.setApplicationDisplayMetrics(activity, w, h); + QtDisplayManager.handleOrientationChanges(activity, true); + } +} diff --git a/src/android/jar/src/org/qtproject/qt/android/QtWindow.java b/src/android/jar/src/org/qtproject/qt/android/QtWindow.java index ba19e8dba16..757b00d93f8 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtWindow.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtWindow.java @@ -4,8 +4,6 @@ package org.qtproject.qt.android; import android.content.Context; -import android.os.Handler; -import android.os.Looper; import android.util.Log; import android.view.Surface; import android.view.View; @@ -18,14 +16,26 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba private QtSurface m_surface; private View m_nativeView; - private Handler m_androidHandler; + private HashMap m_childWindows = new HashMap(); + private QtWindow m_parentWindow; private static native void setSurface(int windowId, Surface surface); - public QtWindow(Context context) + public QtWindow(Context context, QtWindow parentWindow) { super(context); setId(View.generateViewId()); + + setParent(parentWindow); + } + + void setVisible(boolean visible) { + QtNative.runAction(() -> { + if (visible) + setVisibility(View.VISIBLE); + else + setVisibility(View.INVISIBLE); + }); } @Override @@ -34,6 +44,12 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba setSurface(getId(), surface); } + public void removeWindow() + { + if (m_parentWindow != null) + m_parentWindow.removeChildWindow(getId()); + } + public void createSurface(final boolean onTop, final int x, final int y, final int w, final int h, final int imageDepth) @@ -44,11 +60,20 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba if (m_surface != null) removeView(m_surface); - QtSurface surface = new QtSurface(getContext(), - QtWindow.this, QtWindow.this.getId(), - onTop, imageDepth); - surface.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y)); + setLayoutParams(new QtLayout.LayoutParams(w, h, x, y)); + // TODO currently setting child windows to onTop, since their surfaces + // now get created earlier than the parents -> they are behind the parent window + // without this, and SurfaceView z-ordering is limited + boolean tempOnTop = onTop || (m_parentWindow != null); + QtSurface surface = new QtSurface(getContext(), QtWindow.this, + QtWindow.this.getId(), tempOnTop, imageDepth); + surface.setLayoutParams(new QtLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + + // The QtSurface of this window will be added as the first of the stack. + // All other views are stacked based on the order they are created. addView(surface, 0); m_surface = surface; } @@ -68,16 +93,34 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba }); } - public void setSurfaceGeometry(final int x, final int y, final int w, final int h) + public void setGeometry(final int x, final int y, final int w, final int h) { QtNative.runAction(new Runnable() { @Override public void run() { - QtLayout.LayoutParams lp = new QtLayout.LayoutParams(w, h, x, y); - if (m_surface != null) - m_surface.setLayoutParams(lp); - else if (m_nativeView != null) - m_nativeView.setLayoutParams(lp); + QtWindow.this.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y)); + } + }); + } + + public void addChildWindow(QtWindow window) + { + QtNative.runAction(new Runnable() { + @Override + public void run() { + m_childWindows.put(window.getId(), window); + addView(window, getChildCount()); + } + }); + } + + public void removeChildWindow(int id) + { + QtNative.runAction(new Runnable() { + @Override + public void run() { + if (m_childWindows.containsKey(id)) + removeView(m_childWindows.remove(id)); } }); } @@ -92,13 +135,35 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba removeView(m_nativeView); m_nativeView = view; - m_nativeView.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y)); - + QtWindow.this.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y)); + m_nativeView.setLayoutParams(new QtLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); addView(m_nativeView); } }); } + public void bringChildToFront(int id) + { + View view = m_childWindows.get(id); + if (view != null) { + if (getChildCount() > 0) + moveChild(view, getChildCount() - 1); + } + } + + public void bringChildToBack(int id) { + QtNative.runAction(new Runnable() { + @Override + public void run() { + View view = m_childWindows.get(id); + if (view != null) { + moveChild(view, 0); + } + } + }); + } + public void removeNativeView() { QtNative.runAction(new Runnable() { @@ -111,4 +176,22 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba } }); } + + void setParent(QtWindow parentWindow) + { + if (m_parentWindow == parentWindow) + return; + + if (m_parentWindow != null) + m_parentWindow.removeChildWindow(getId()); + + m_parentWindow = parentWindow; + if (m_parentWindow != null) + m_parentWindow.addChildWindow(this); + } + + QtWindow parent() + { + return m_parentWindow; + } } diff --git a/src/plugins/platforms/android/qandroidplatformforeignwindow.cpp b/src/plugins/platforms/android/qandroidplatformforeignwindow.cpp index 0fc83d05c38..038af6b09b8 100644 --- a/src/plugins/platforms/android/qandroidplatformforeignwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformforeignwindow.cpp @@ -32,7 +32,7 @@ void QAndroidPlatformForeignWindow::setGeometry(const QRect &rect) QAndroidPlatformWindow::setGeometry(rect); if (m_nativeViewInserted) - setSurfaceGeometry(rect); + setNativeGeometry(rect); } void QAndroidPlatformForeignWindow::setVisible(bool visible) @@ -63,11 +63,6 @@ void QAndroidPlatformForeignWindow::applicationStateChanged(Qt::ApplicationState QAndroidPlatformWindow::applicationStateChanged(state); } -void QAndroidPlatformForeignWindow::setParent(const QPlatformWindow *window) -{ - Q_UNUSED(window); -} - void QAndroidPlatformForeignWindow::addViewToWindow() { jint x = 0, y = 0, w = -1, h = -1; diff --git a/src/plugins/platforms/android/qandroidplatformforeignwindow.h b/src/plugins/platforms/android/qandroidplatformforeignwindow.h index 19588c7ad7e..52b9ac3a4f4 100644 --- a/src/plugins/platforms/android/qandroidplatformforeignwindow.h +++ b/src/plugins/platforms/android/qandroidplatformforeignwindow.h @@ -20,7 +20,6 @@ public: void setGeometry(const QRect &rect) override; void setVisible(bool visible) override; void applicationStateChanged(Qt::ApplicationState state) override; - void setParent(const QPlatformWindow *window) override; bool isForeignWindow() const override { return true; } private: diff --git a/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp b/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp index 7ffa79e2c1e..13d14eb3913 100644 --- a/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformopenglwindow.cpp @@ -45,7 +45,9 @@ void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect) m_oldGeometry = geometry(); QAndroidPlatformWindow::setGeometry(rect); - setSurfaceGeometry(rect); + + + setNativeGeometry(rect); QRect availableGeometry = screen()->availableGeometry(); if (rect.width() > 0 @@ -59,7 +61,7 @@ void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect) EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config) { if (QAndroidEventDispatcherStopper::stopped() || - QGuiApplication::applicationState() == Qt::ApplicationSuspended || !window()->isTopLevel()) { + QGuiApplication::applicationState() == Qt::ApplicationSuspended) { return m_eglSurface; } diff --git a/src/plugins/platforms/android/qandroidplatformscreen.h b/src/plugins/platforms/android/qandroidplatformscreen.h index e7ce6ed59f0..84efc43630b 100644 --- a/src/plugins/platforms/android/qandroidplatformscreen.h +++ b/src/plugins/platforms/android/qandroidplatformscreen.h @@ -44,7 +44,6 @@ public: void removeWindow(QAndroidPlatformWindow *window); void raise(QAndroidPlatformWindow *window); void lower(QAndroidPlatformWindow *window); - void topVisibleWindowChanged(); int displayId() const override; diff --git a/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp b/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp index 43b3a903734..4bf4f44fa10 100644 --- a/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformvulkanwindow.cpp @@ -39,7 +39,8 @@ void QAndroidPlatformVulkanWindow::setGeometry(const QRect &rect) m_oldGeometry = geometry(); QAndroidPlatformWindow::setGeometry(rect); - setSurfaceGeometry(rect); + if (m_surfaceCreated) + setNativeGeometry(rect); QRect availableGeometry = screen()->availableGeometry(); if (rect.width() > 0 diff --git a/src/plugins/platforms/android/qandroidplatformwindow.cpp b/src/plugins/platforms/android/qandroidplatformwindow.cpp index 1e30799ae45..9a633e97afe 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformwindow.cpp @@ -19,10 +19,9 @@ Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window") Q_CONSTINIT static QBasicAtomicInt winIdGenerator = Q_BASIC_ATOMIC_INITIALIZER(0); QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window) - : QPlatformWindow(window), m_nativeQtWindow(QNativeInterface::QAndroidApplication::context()), + : QPlatformWindow(window), m_nativeQtWindow(nullptr), m_nativeParentQtWindow(nullptr), m_androidSurfaceObject(nullptr) { - m_nativeViewId = m_nativeQtWindow.callMethod("getId"); m_windowFlags = Qt::Widget; m_windowState = Qt::WindowNoState; // the surfaceType is overwritten in QAndroidPlatformOpenGLWindow ctor so let's save @@ -48,22 +47,44 @@ QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window) if (requestedNativeGeometry != finalNativeGeometry) setGeometry(finalNativeGeometry); } - platformScreen()->addWindow(this); + + if (parent()) + m_nativeParentQtWindow = static_cast(parent())->nativeWindow(); + + QNativeInterface::QAndroidApplication::runOnAndroidMainThread([this]() { + m_nativeQtWindow = QJniObject::construct( + QNativeInterface::QAndroidApplication::context(), + m_nativeParentQtWindow); + m_nativeViewId = m_nativeQtWindow.callMethod("getId"); + }).waitForFinished(); + + if (window->isTopLevel()) + platformScreen()->addWindow(this); } QAndroidPlatformWindow::~QAndroidPlatformWindow() { - platformScreen()->removeWindow(this); + if (window()->isTopLevel()) + platformScreen()->removeWindow(this); } void QAndroidPlatformWindow::lower() { + if (m_nativeParentQtWindow.isValid()) { + m_nativeParentQtWindow.callMethod("bringChildToBack", nativeViewId()); + return; + } platformScreen()->lower(this); } void QAndroidPlatformWindow::raise() { + if (m_nativeParentQtWindow.isValid()) { + m_nativeParentQtWindow.callMethod("bringChildToFront", nativeViewId()); + QWindowSystemInterface::handleFocusWindowChanged(window(), Qt::ActiveWindowFocusReason); + return; + } updateSystemUiVisibility(); platformScreen()->raise(this); } @@ -87,16 +108,20 @@ void QAndroidPlatformWindow::setGeometry(const QRect &rect) void QAndroidPlatformWindow::setVisible(bool visible) { + m_nativeQtWindow.callMethod("setVisible", visible); + if (visible) { - updateSystemUiVisibility(); - if ((m_windowState & Qt::WindowFullScreen) - || ((m_windowState & Qt::WindowMaximized) && (window()->flags() & Qt::MaximizeUsingFullscreenGeometryHint))) { - setGeometry(platformScreen()->geometry()); - } else if (m_windowState & Qt::WindowMaximized) { - setGeometry(platformScreen()->availableGeometry()); + if (window()->isTopLevel()) { + updateSystemUiVisibility(); + if ((m_windowState & Qt::WindowFullScreen) + || ((m_windowState & Qt::WindowMaximized) && (window()->flags() & Qt::MaximizeUsingFullscreenGeometryHint))) { + setGeometry(platformScreen()->geometry()); + } else if (m_windowState & Qt::WindowMaximized) { + setGeometry(platformScreen()->availableGeometry()); + } + requestActivateWindow(); } - requestActivateWindow(); - } else if (window() == qGuiApp->focusWindow()) { + } else if (window()->isTopLevel() && window() == qGuiApp->focusWindow()) { platformScreen()->topVisibleWindowChanged(); } @@ -132,13 +157,22 @@ Qt::WindowFlags QAndroidPlatformWindow::windowFlags() const void QAndroidPlatformWindow::setParent(const QPlatformWindow *window) { - // even though we do not yet support child windows properly, any windows getting a parent - // should be removed from screen's window stack which is only for top level windows, - // and respectively any window becoming top level should go in there + using namespace QtJniTypes; if (window) { - platformScreen()->removeWindow(this); + // If we were a top level window, remove from screen + if (!m_nativeParentQtWindow.isValid()) + platformScreen()->removeWindow(this); + + const QAndroidPlatformWindow *androidWindow = + static_cast(window); + const QtWindow parentWindow = androidWindow->nativeWindow(); + // If this was a child window of another window, the java method takes care of that + m_nativeQtWindow.callMethod("setParent", parentWindow.object()); + m_nativeParentQtWindow = parentWindow; } else { + m_nativeQtWindow.callMethod("setParent", nullptr); platformScreen()->addWindow(this); + m_nativeParentQtWindow = QJniObject(); } } @@ -154,6 +188,7 @@ void QAndroidPlatformWindow::propagateSizeHints() void QAndroidPlatformWindow::requestActivateWindow() { + // raise() will handle differences between top level and child windows, and requesting focus if (!blockedByModal()) raise(); } @@ -214,7 +249,7 @@ void QAndroidPlatformWindow::destroySurface() } } -void QAndroidPlatformWindow::setSurfaceGeometry(const QRect &geometry) +void QAndroidPlatformWindow::setNativeGeometry(const QRect &geometry) { if (!m_surfaceCreated) return; @@ -229,7 +264,7 @@ void QAndroidPlatformWindow::setSurfaceGeometry(const QRect &geometry) w = geometry.width(); h = geometry.height(); } - m_nativeQtWindow.callMethod("setSurfaceGeometry", x, y, w, h); + m_nativeQtWindow.callMethod("setGeometry", x, y, w, h); } void QAndroidPlatformWindow::onSurfaceChanged(QtJniTypes::Surface surface) diff --git a/src/plugins/platforms/android/qandroidplatformwindow.h b/src/plugins/platforms/android/qandroidplatformwindow.h index 88949d84daa..6f85bbf9ece 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.h +++ b/src/plugins/platforms/android/qandroidplatformwindow.h @@ -65,7 +65,7 @@ protected: void unlockSurface() { m_surfaceMutex.unlock(); } void createSurface(); void destroySurface(); - void setSurfaceGeometry(const QRect &geometry); + void setNativeGeometry(const QRect &geometry); void sendExpose() const; bool blockedByModal() const; @@ -76,6 +76,7 @@ protected: WId m_windowId; QtJniTypes::QtWindow m_nativeQtWindow; + QtJniTypes::QtWindow m_nativeParentQtWindow; // The Android Surface, accessed from multiple threads, guarded by m_surfaceMutex QtJniTypes::Surface m_androidSurfaceObject; QWaitCondition m_surfaceWaitCondition; diff --git a/tests/manual/embeddedwindows/main.cpp b/tests/manual/embeddedwindows/main.cpp index 904839cfa3b..0d897abd9e4 100644 --- a/tests/manual/embeddedwindows/main.cpp +++ b/tests/manual/embeddedwindows/main.cpp @@ -3,7 +3,7 @@ #include -#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_OS_WIN) || QT_CONFIG(xcb) +#if defined(Q_OS_MACOS) || defined(Q_OS_IOS) || defined(Q_OS_WIN) || QT_CONFIG(xcb) || defined(ANDROID) #include "../../shared/nativewindow.h" #define HAVE_NATIVE_WINDOW #endif diff --git a/tests/shared/nativewindow.h b/tests/shared/nativewindow.h index 8b5ad97836c..bddc0f12163 100644 --- a/tests/shared/nativewindow.h +++ b/tests/shared/nativewindow.h @@ -14,6 +14,12 @@ # include #elif QT_CONFIG(xcb) # include +#elif defined(ANDROID) +# include +# include +# include +Q_DECLARE_JNI_CLASS(View, "android/view/View") +Q_DECLARE_JNI_CLASS(ViewParent, "android/view/ViewParent") #endif class NativeWindow @@ -28,6 +34,8 @@ public: using Handle = HWND; #elif QT_CONFIG(xcb) using Handle = xcb_window_t; +#elif defined(ANDROID) + using Handle = QtJniTypes::View;; #endif NativeWindow(); @@ -249,6 +257,48 @@ void NativeWindow::setParent(WId parent) parent ? Handle(parent) : screen->root, 0, 0); } +#elif defined (ANDROID) +NativeWindow::NativeWindow() +{ + m_handle = QJniObject::construct( + QNativeInterface::QAndroidApplication::context()); + m_handle.callMethod("setBackgroundColor", 0xffffaaff); +} + +NativeWindow::~NativeWindow() +{ +} + +NativeWindow::operator WId() const +{ + return reinterpret_cast(m_handle.object()); +} + +void NativeWindow::setGeometry(const QRect &rect) +{ + // No-op, the view geometry is handled by the QWindow constructed from it +} + +QRect NativeWindow::geometry() const +{ + int x = m_handle.callMethod("getX"); + int y = m_handle.callMethod("getY"); + int w = m_handle.callMethod("getWidth"); + int h = m_handle.callMethod("getHeight"); + return QRect(x, y, w, h); +} + +WId NativeWindow::parentWinId() const +{ + // TODO note, the returned object is a ViewParent, not necessarily + // a View - what is this used for? + using namespace QtJniTypes; + ViewParent parentView = m_handle.callMethod("getParent"); + if (parentView.isValid()) + return reinterpret_cast(parentView.object()); + return 0L; +} + #endif #endif // NATIVEWINDOW_H