Android: Use TextureView when multiple windows present
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ø <tor.arne.vestbo@qt.io>
This commit is contained in:
parent
b2e44a2d1d
commit
cca81a6636
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,13 @@
|
||||
// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
|
||||
// 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);
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
// Copyright (C) 2014 BogDan Vatra <bogdan@kde.org>
|
||||
// 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) {
|
||||
}
|
||||
}
|
@ -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<Integer, QtWindow> m_childWindows = new HashMap<Integer, QtWindow>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -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<void>("createSurface", windowStaysOnTop, x, y, w, h, 32);
|
||||
m_nativeQtWindow.callMethod<void>("createSurface", windowStaysOnTop, x, y, w, h, 32, isOpaque,
|
||||
m_surfaceContainerType);
|
||||
m_surfaceCreated = true;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user