From cca81a663661ef1fce5f4a9f4c5a5f5fd1381325 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tinja=20Paavosepp=C3=A4?= Date: Tue, 17 Oct 2023 11:47:49 +0300 Subject: Android: Use TextureView when multiple windows present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SurfaceView class is not the best option for when we have multiple windows, as the created Surface can only either be below the window or on top of it, it is not a part of the view hierarchy. Replace the SurfaceView with TextureView when there are more than one window. This way the surface will be a part of the view hierarchy, and will respect z-ordering. When there is only one window, keep using the SurfaceView approach to limit the effect on existing apps, as well as enable some of the benefits SurfaceView has for e.g. game and multimedia apps, such as HDR ability. Move touch handling from QtSurface to QtWindow, so touches are handled also when using TextureView instead of QtSurface aka SurfaceView. Pick-to: 6.7 Task-number: QTBUG-118142 Change-Id: I37dcaf0fb656cfc1ff2eca0a3bfe7259f607411c Reviewed-by: Tor Arne Vestbø --- src/android/jar/CMakeLists.txt | 2 + .../jar/src/org/qtproject/qt/android/QtLayout.java | 43 ++++++++++++- .../src/org/qtproject/qt/android/QtSurface.java | 49 +-------------- .../qtproject/qt/android/QtSurfaceInterface.java | 13 ++++ .../org/qtproject/qt/android/QtTextureView.java | 52 ++++++++++++++++ .../jar/src/org/qtproject/qt/android/QtWindow.java | 71 +++++++++++++++------- .../platforms/android/qandroidplatformwindow.cpp | 12 +++- .../platforms/android/qandroidplatformwindow.h | 6 ++ 8 files changed, 177 insertions(+), 71 deletions(-) create mode 100644 src/android/jar/src/org/qtproject/qt/android/QtSurfaceInterface.java create mode 100644 src/android/jar/src/org/qtproject/qt/android/QtTextureView.java diff --git a/src/android/jar/CMakeLists.txt b/src/android/jar/CMakeLists.txt index a4463291a9..7c062120f0 100644 --- a/src/android/jar/CMakeLists.txt +++ b/src/android/jar/CMakeLists.txt @@ -21,7 +21,9 @@ set(java_sources src/org/qtproject/qt/android/QtLayout.java src/org/qtproject/qt/android/QtMessageDialogHelper.java src/org/qtproject/qt/android/QtNative.java + src/org/qtproject/qt/android/QtSurfaceInterface.java src/org/qtproject/qt/android/QtSurface.java + src/org/qtproject/qt/android/QtTextureView.java src/org/qtproject/qt/android/QtThread.java src/org/qtproject/qt/android/extras/QtAndroidBinder.java src/org/qtproject/qt/android/extras/QtAndroidServiceConnection.java 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 5f560db7a5..511f733479 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtLayout.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtLayout.java @@ -10,16 +10,31 @@ import android.os.Build; import android.util.AttributeSet; import android.util.DisplayMetrics; import android.view.Display; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -class QtLayout extends ViewGroup -{ +class QtLayout extends ViewGroup { + + interface QtTouchListener { + public boolean onTouchEvent(MotionEvent event); + public boolean onTrackballEvent(MotionEvent event); + public boolean onGenericMotionEvent(MotionEvent event); + } + + private QtTouchListener m_touchListener; + public QtLayout(Context context) { super(context); } + public QtLayout(Context context, QtTouchListener listener) + { + super(context); + m_touchListener = listener; + } + public QtLayout(Context context, AttributeSet attrs) { super(context, attrs); @@ -30,6 +45,30 @@ class QtLayout extends ViewGroup super(context, attrs, defStyle); } + @Override + public boolean onTouchEvent(MotionEvent event) + { + if (m_touchListener != null) { + event.setLocation(event.getX() + getX(), event.getY() + getY()); + return m_touchListener.onTouchEvent(event); + } + return false; + } + @Override + public boolean onTrackballEvent(MotionEvent event) + {; + if (m_touchListener != null) + return m_touchListener.onTrackballEvent(event); + return false; + } + @Override + public boolean onGenericMotionEvent(MotionEvent event) + { + if (m_touchListener != null) + return m_touchListener.onGenericMotionEvent(event); + return false; + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { diff --git a/src/android/jar/src/org/qtproject/qt/android/QtSurface.java b/src/android/jar/src/org/qtproject/qt/android/QtSurface.java index 7682d87d96..3165de4811 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtSurface.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtSurface.java @@ -7,8 +7,6 @@ package org.qtproject.qt.android; import android.annotation.SuppressLint; import android.content.Context; import android.graphics.PixelFormat; -import android.view.GestureDetector; -import android.view.MotionEvent; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; @@ -16,17 +14,9 @@ import android.view.SurfaceView; @SuppressLint("ViewConstructor") class QtSurface extends SurfaceView implements SurfaceHolder.Callback { - private final GestureDetector m_gestureDetector; - private Object m_accessibilityDelegate = null; - private SurfaceChangedCallback m_surfaceCallback; - private final int m_windowId; + private QtSurfaceInterface m_surfaceCallback; - interface SurfaceChangedCallback { - void onSurfaceChanged(Surface surface); - } - - public QtSurface(Context context, SurfaceChangedCallback surfaceCallback, int id, boolean onTop, - int imageDepth) + public QtSurface(Context context, QtSurfaceInterface surfaceCallback, boolean onTop, int imageDepth) { super(context); setFocusable(false); @@ -38,15 +28,6 @@ class QtSurface extends SurfaceView implements SurfaceHolder.Callback getHolder().setFormat(PixelFormat.RGB_565); else getHolder().setFormat(PixelFormat.RGBA_8888); - - m_windowId = id; - m_gestureDetector = - new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { - public void onLongPress(MotionEvent event) { - QtInputDelegate.longPress(m_windowId, (int) event.getX(), (int) event.getY()); - } - }); - m_gestureDetector.setIsLongpressEnabled(true); } @Override @@ -69,30 +50,4 @@ class QtSurface extends SurfaceView implements SurfaceHolder.Callback if (m_surfaceCallback != null) m_surfaceCallback.onSurfaceChanged(null); } - - @Override - public boolean onTouchEvent(MotionEvent event) - { - // QTBUG-65927 - // Fix event positions depending on Surface position. - // In case when Surface is moved, we should also add this move to event position - event.setLocation(event.getX() + getX(), event.getY() + getY()); - - QtInputDelegate.sendTouchEvent(event, m_windowId); - m_gestureDetector.onTouchEvent(event); - return true; - } - - @Override - public boolean onTrackballEvent(MotionEvent event) - { - QtInputDelegate.sendTrackballEvent(event, m_windowId); - return true; - } - - @Override - public boolean onGenericMotionEvent(MotionEvent event) - { - return QtInputDelegate.sendGenericMotionEvent(event, m_windowId); - } } diff --git a/src/android/jar/src/org/qtproject/qt/android/QtSurfaceInterface.java b/src/android/jar/src/org/qtproject/qt/android/QtSurfaceInterface.java new file mode 100644 index 0000000000..8df442f730 --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt/android/QtSurfaceInterface.java @@ -0,0 +1,13 @@ +// Copyright (C) 2014 BogDan Vatra +// Copyright (C) 2016 The Qt Company Ltd. +// 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.view.Surface; + + +public interface QtSurfaceInterface +{ + void onSurfaceChanged(Surface surface); +} diff --git a/src/android/jar/src/org/qtproject/qt/android/QtTextureView.java b/src/android/jar/src/org/qtproject/qt/android/QtTextureView.java new file mode 100644 index 0000000000..828838a9f0 --- /dev/null +++ b/src/android/jar/src/org/qtproject/qt/android/QtTextureView.java @@ -0,0 +1,52 @@ +// Copyright (C) 2014 BogDan Vatra +// Copyright (C) 2016 The Qt Company Ltd. +// 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.content.Context; +import android.graphics.PixelFormat; +import android.graphics.SurfaceTexture; +import android.util.Log; +import android.view.Surface; +import android.view.TextureView; + +public class QtTextureView extends TextureView implements TextureView.SurfaceTextureListener +{ + private QtSurfaceInterface m_surfaceCallback; + private boolean m_staysOnTop; + private Surface m_surface; + + public QtTextureView(Context context, QtSurfaceInterface surfaceCallback, boolean isOpaque) + { + super(context); + setFocusable(false); + setFocusableInTouchMode(false); + m_surfaceCallback = surfaceCallback; + setSurfaceTextureListener(this); + setOpaque(isOpaque); + setSurfaceTexture(new SurfaceTexture(false)); + } + + @Override + public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) { + m_surface = new Surface(surfaceTexture); + m_surfaceCallback.onSurfaceChanged(m_surface); + } + + @Override + public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) { + m_surface = new Surface(surfaceTexture); + m_surfaceCallback.onSurfaceChanged(m_surface); + } + + @Override + public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { + m_surfaceCallback.onSurfaceChanged(null); + return true; + } + + @Override + public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { + } +} 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 79e7b96cb9..44ba257ce2 100644 --- a/src/android/jar/src/org/qtproject/qt/android/QtWindow.java +++ b/src/android/jar/src/org/qtproject/qt/android/QtWindow.java @@ -4,6 +4,8 @@ package org.qtproject.qt.android; import android.content.Context; +import android.view.GestureDetector; +import android.view.MotionEvent; import android.util.Log; import android.view.Surface; import android.view.View; @@ -11,15 +13,16 @@ import android.view.ViewGroup; import java.util.HashMap; -public class QtWindow implements QtSurface.SurfaceChangedCallback { +public class QtWindow implements QtSurfaceInterface, QtLayout.QtTouchListener { private final static String TAG = "QtWindow"; + private View m_surfaceContainer; private QtLayout m_layout; - private QtSurface m_surface; private View m_nativeView; private HashMap m_childWindows = new HashMap(); private QtWindow m_parentWindow; private int m_id; + private GestureDetector m_gestureDetector; private static native void setSurface(int windowId, Surface surface); @@ -27,8 +30,15 @@ public class QtWindow implements QtSurface.SurfaceChangedCallback { { m_id = View.generateViewId(); QtNative.runAction(() -> { - m_layout = new QtLayout(context); + m_layout = new QtLayout(context, this); setParent(parentWindow); + m_gestureDetector = + new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { + public void onLongPress(MotionEvent event) { + QtInputDelegate.longPress(getId(), (int) event.getX(), (int) event.getY()); + } + }); + m_gestureDetector.setIsLongpressEnabled(true); }); } @@ -57,6 +67,27 @@ public class QtWindow implements QtSurface.SurfaceChangedCallback { setSurface(getId(), surface); } + @Override + public boolean onTouchEvent(MotionEvent event) + { + QtInputDelegate.sendTouchEvent(event, getId()); + m_gestureDetector.onTouchEvent(event); + return true; + } + + @Override + public boolean onTrackballEvent(MotionEvent event) + { + QtInputDelegate.sendTrackballEvent(event, getId()); + return true; + } + + @Override + public boolean onGenericMotionEvent(MotionEvent event) + { + return QtInputDelegate.sendGenericMotionEvent(event, getId()); + } + public void removeWindow() { if (m_parentWindow != null) @@ -65,30 +96,28 @@ public class QtWindow implements QtSurface.SurfaceChangedCallback { public void createSurface(final boolean onTop, final int x, final int y, final int w, final int h, - final int imageDepth) + final int imageDepth, final boolean isOpaque, + final int surfaceContainerType) // TODO constant for type { QtNative.runAction(new Runnable() { @Override public void run() { - if (m_surface != null) - m_layout.removeView(m_surface); + if (m_surfaceContainer != null) + m_layout.removeView(m_surfaceContainer); m_layout.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(m_layout.getContext(), QtWindow.this, - QtWindow.this.getId(), tempOnTop, imageDepth); - surface.setLayoutParams(new QtLayout.LayoutParams( + if (surfaceContainerType == 0) { + m_surfaceContainer = new QtSurface(m_layout.getContext(), QtWindow.this, + onTop, imageDepth); + } else { + m_surfaceContainer = new QtTextureView(m_layout.getContext(), QtWindow.this, isOpaque); + } + m_surfaceContainer.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. + // The surface container of this window will be added as the first of the stack. // All other views are stacked based on the order they are created. - m_layout.addView(surface, 0); - m_surface = surface; + m_layout.addView(m_surfaceContainer, 0); } }); } @@ -98,9 +127,9 @@ public class QtWindow implements QtSurface.SurfaceChangedCallback { QtNative.runAction(new Runnable() { @Override public void run() { - if (m_surface != null) { - m_layout.removeView(m_surface); - m_surface = null; + if (m_surfaceContainer != null) { + m_layout.removeView(m_surfaceContainer); + m_surfaceContainer = null; } } }); diff --git a/src/plugins/platforms/android/qandroidplatformwindow.cpp b/src/plugins/platforms/android/qandroidplatformwindow.cpp index 6681f1d20d..0756fb115f 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.cpp +++ b/src/plugins/platforms/android/qandroidplatformwindow.cpp @@ -55,6 +55,14 @@ QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window) if (window->isTopLevel()) platformScreen()->addWindow(this); + + // TODO should handle case where this changes at runtime -> need to change existing window + // into TextureView (or perhaps not, if the parent window would be SurfaceView, as long as + // onTop was false it would stay below the children) + if (platformScreen()->windows().size() > 1) + m_surfaceContainerType = SurfaceContainer::TextureView; + else + m_surfaceContainerType = SurfaceContainer::SurfaceView; } QAndroidPlatformWindow::~QAndroidPlatformWindow() @@ -236,8 +244,10 @@ void QAndroidPlatformWindow::createSurface() } const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint); + const bool isOpaque = format().hasAlpha() || (0.0 < window()->opacity() < 1.0); - m_nativeQtWindow.callMethod("createSurface", windowStaysOnTop, x, y, w, h, 32); + m_nativeQtWindow.callMethod("createSurface", windowStaysOnTop, x, y, w, h, 32, isOpaque, + m_surfaceContainerType); m_surfaceCreated = true; } diff --git a/src/plugins/platforms/android/qandroidplatformwindow.h b/src/plugins/platforms/android/qandroidplatformwindow.h index 1c95ca123b..3fc5526c1c 100644 --- a/src/plugins/platforms/android/qandroidplatformwindow.h +++ b/src/plugins/platforms/android/qandroidplatformwindow.h @@ -26,6 +26,11 @@ class QAndroidPlatformScreen; class QAndroidPlatformWindow: public QPlatformWindow { public: + enum class SurfaceContainer { + SurfaceView, + TextureView + }; + explicit QAndroidPlatformWindow(QWindow *window); ~QAndroidPlatformWindow(); void lower() override; @@ -76,6 +81,7 @@ protected: int m_nativeViewId = -1; QtJniTypes::QtWindow m_nativeQtWindow; + SurfaceContainer m_surfaceContainerType = SurfaceContainer::SurfaceView; QtJniTypes::QtWindow m_nativeParentQtWindow; // The Android Surface, accessed from multiple threads, guarded by m_surfaceMutex QtJniTypes::Surface m_androidSurfaceObject; -- cgit v1.2.3