Android: Make QtLayout per window instead of per screen

Each QAndroidPlatformWindow has its own QtLayout,
instead of one for the whole app/screen. This paves the
way for addition of child windows.

Task-number: QTBUG-116187
Change-Id: I36c68cea1a5f27ded3696bcfc2fbc04d9a8ce79e
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
Tinja Paavoseppä 2023-09-05 09:01:46 +03:00
parent 0bfec6cd11
commit 0a92d881bb
15 changed files with 385 additions and 382 deletions

View File

@ -29,6 +29,7 @@ set(java_sources
src/org/qtproject/qt/android/QtClipboardManager.java src/org/qtproject/qt/android/QtClipboardManager.java
src/org/qtproject/qt/android/QtDisplayManager.java src/org/qtproject/qt/android/QtDisplayManager.java
src/org/qtproject/qt/android/UsedFromNativeCode.java src/org/qtproject/qt/android/UsedFromNativeCode.java
src/org/qtproject/qt/android/QtWindow.java
) )
qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android qt_internal_add_jar(Qt${QtBase_VERSION_MAJOR}Android

View File

@ -36,9 +36,7 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate
private View m_view = null; private View m_view = null;
private final AccessibilityManager m_manager; private final AccessibilityManager m_manager;
private final QtActivityDelegate m_activityDelegate; private final QtLayout m_layout;
private final Activity m_activity;
private final ViewGroup m_layout;
// The accessible object that currently has the "accessibility focus" // The accessible object that currently has the "accessibility focus"
// usually indicated by a yellow rectangle on screen. // usually indicated by a yellow rectangle on screen.
@ -62,13 +60,11 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate
} }
} }
public QtAccessibilityDelegate(Activity activity, ViewGroup layout, QtActivityDelegate activityDelegate) public QtAccessibilityDelegate(QtLayout layout)
{ {
m_activity = activity;
m_layout = layout; m_layout = layout;
m_activityDelegate = activityDelegate;
m_manager = (AccessibilityManager) m_activity.getSystemService(Context.ACCESSIBILITY_SERVICE); m_manager = (AccessibilityManager) m_layout.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
if (m_manager != null) { if (m_manager != null) {
AccessibilityManagerListener accServiceListener = new AccessibilityManagerListener(); AccessibilityManagerListener accServiceListener = new AccessibilityManagerListener();
if (!m_manager.addAccessibilityStateChangeListener(accServiceListener)) if (!m_manager.addAccessibilityStateChangeListener(accServiceListener))
@ -89,7 +85,7 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate
try { try {
View view = m_view; View view = m_view;
if (view == null) { if (view == null) {
view = new View(m_activity); view = new View(m_layout.getContext());
view.setId(View.NO_ID); view.setId(View.NO_ID);
} }
@ -105,7 +101,7 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate
// if all is fine, add it to the layout // if all is fine, add it to the layout
if (m_view == null) { if (m_view == null) {
//m_layout.addAccessibilityView(view); //m_layout.addAccessibilityView(view);
m_layout.addView(view, m_activityDelegate.getSurfaceCount(), m_layout.addView(view, m_layout.getChildCount(),
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
} }
m_view = view; m_view = view;
@ -289,7 +285,7 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate
return null; return null;
} }
if (m_activityDelegate.getSurfaceCount() == 0) if (m_layout.getChildCount() == 0)
return null; return null;
final AccessibilityEvent event = AccessibilityEvent.obtain(eventType); final AccessibilityEvent event = AccessibilityEvent.obtain(eventType);
@ -356,7 +352,7 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate
// Spit out the entire hierarchy for debugging purposes // Spit out the entire hierarchy for debugging purposes
// dumpNodes(-1); // dumpNodes(-1);
if (m_activityDelegate.getSurfaceCount() != 0) { if (m_layout.getChildCount() == 0) {
int[] ids = QtNativeAccessibility.childIdListForAccessibleObject(-1); int[] ids = QtNativeAccessibility.childIdListForAccessibleObject(-1);
for (int id : ids) for (int id : ids)
result.addChild(m_view, id); result.addChild(m_view, id);
@ -388,7 +384,7 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate
node.setClassName(m_view.getClass().getName() + DEFAULT_CLASS_NAME); node.setClassName(m_view.getClass().getName() + DEFAULT_CLASS_NAME);
node.setPackageName(m_view.getContext().getPackageName()); node.setPackageName(m_view.getContext().getPackageName());
if (m_activityDelegate.getSurfaceCount() == 0 || !QtNativeAccessibility.populateNode(virtualViewId, node)) { if (m_layout.getChildCount() == 0 || !QtNativeAccessibility.populateNode(virtualViewId, node)) {
return node; return node;
} }
@ -439,7 +435,7 @@ class QtAccessibilityDelegate extends View.AccessibilityDelegate
@Override @Override
public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId)
{ {
if (virtualViewId == View.NO_ID || m_activityDelegate.getSurfaceCount() == 0) { if (virtualViewId == View.NO_ID || m_layout.getChildCount() == 0) {
return getNodeForView(); return getNodeForView();
} }
return getNodeForVirtualViewId(virtualViewId); return getNodeForVirtualViewId(virtualViewId);

View File

@ -11,6 +11,7 @@ import android.content.pm.PackageManager;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Build; import android.os.Build;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
@ -36,9 +37,8 @@ class QtActivityDelegate
{ {
private Activity m_activity; private Activity m_activity;
private HashMap<Integer, QtSurface> m_surfaces = null;
private HashMap<Integer, View> m_nativeViews = null;
private QtLayout m_layout = null; private QtLayout m_layout = null;
private HashMap<Integer, QtWindow> m_topLevelWindows;
private ImageView m_splashScreen = null; private ImageView m_splashScreen = null;
private boolean m_splashScreenSticky = false; private boolean m_splashScreenSticky = false;
@ -48,6 +48,7 @@ class QtActivityDelegate
private QtDisplayManager m_displayManager = null; private QtDisplayManager m_displayManager = null;
private QtInputDelegate m_inputDelegate = null; private QtInputDelegate m_inputDelegate = null;
private boolean m_membersInitialized = false;
QtActivityDelegate(Activity activity) QtActivityDelegate(Activity activity)
{ {
@ -55,6 +56,7 @@ class QtActivityDelegate
QtNative.setActivity(m_activity); QtNative.setActivity(m_activity);
setActionBarVisibility(false); setActionBarVisibility(false);
setActivityBackgroundDrawable();
} }
QtDisplayManager displayManager() { QtDisplayManager displayManager() {
@ -116,7 +118,7 @@ class QtActivityDelegate
public void startNativeApplication(String appParams, String mainLib) public void startNativeApplication(String appParams, String mainLib)
{ {
if (m_surfaces != null) if (m_membersInitialized)
return; return;
initMembers(); initMembers();
@ -134,6 +136,8 @@ class QtActivityDelegate
private void initMembers() private void initMembers()
{ {
m_layout = new QtLayout(m_activity); m_layout = new QtLayout(m_activity);
m_membersInitialized = true;
m_topLevelWindows = new HashMap<Integer, QtWindow>();
m_displayManager = new QtDisplayManager(m_activity); m_displayManager = new QtDisplayManager(m_activity);
m_displayManager.registerDisplayListener(); m_displayManager.registerDisplayListener();
@ -173,8 +177,6 @@ class QtActivityDelegate
e.printStackTrace(); e.printStackTrace();
} }
m_surfaces = new HashMap<>();
m_nativeViews = new HashMap<>();
m_activity.registerForContextMenu(m_layout); m_activity.registerForContextMenu(m_layout);
m_activity.setContentView(m_layout, m_activity.setContentView(m_layout,
new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
@ -293,9 +295,7 @@ class QtActivityDelegate
@UsedFromNativeCode @UsedFromNativeCode
public void initializeAccessibility() public void initializeAccessibility()
{ {
final QtActivityDelegate currentDelegate = this; QtNative.runAction(() -> m_accessibilityDelegate = new QtAccessibilityDelegate(m_layout));
QtNative.runAction(() -> m_accessibilityDelegate = new QtAccessibilityDelegate(m_activity,
m_layout, currentDelegate));
} }
void handleUiModeChange(int uiMode) void handleUiModeChange(int uiMode)
@ -377,135 +377,52 @@ class QtActivityDelegate
} }
@UsedFromNativeCode @UsedFromNativeCode
public void insertNativeView(int id, View view, int x, int y, int w, int h) { public void addTopLevelWindow(final QtWindow window)
QtNative.runAction(() -> { {
if (m_dummyView != null) { QtNative.runAction(()-> {
m_layout.removeView(m_dummyView); if (m_topLevelWindows.size() == 0) {
m_dummyView = null;
}
if (m_nativeViews.containsKey(id))
m_layout.removeView(m_nativeViews.remove(id));
if (w < 0 || h < 0) {
view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
} else {
view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
}
view.setId(id);
m_layout.addView(view);
m_nativeViews.put(id, view);
});
}
@UsedFromNativeCode
public void createSurface(int id, boolean onTop, int x, int y, int w, int h, int imageDepth) {
QtNative.runAction(() -> {
if (m_surfaces.size() == 0) {
TypedValue attr = new TypedValue();
m_activity.getTheme().resolveAttribute(android.R.attr.windowBackground, attr, true);
if (attr.type >= TypedValue.TYPE_FIRST_COLOR_INT && attr.type <= TypedValue.TYPE_LAST_COLOR_INT) {
m_activity.getWindow().setBackgroundDrawable(new ColorDrawable(attr.data));
} else {
m_activity.getWindow().setBackgroundDrawable(m_activity.getResources().getDrawable(attr.resourceId, m_activity.getTheme()));
}
if (m_dummyView != null) { if (m_dummyView != null) {
m_layout.removeView(m_dummyView); m_layout.removeView(m_dummyView);
m_dummyView = null; m_dummyView = null;
} }
} }
if (m_surfaces.containsKey(id)) window.setLayoutParams(new ViewGroup.LayoutParams(
m_layout.removeView(m_surfaces.remove(id)); ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
QtSurface surface = new QtSurface(m_activity, id, onTop, imageDepth); m_layout.addView(window, m_topLevelWindows.size());
if (w < 0 || h < 0) { m_topLevelWindows.put(window.getId(), window);
surface.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
} else {
surface.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
}
// Native views are always inserted in the end of the stack (i.e., on top).
// All other views are stacked based on the order they are created.
final int surfaceCount = getSurfaceCount();
m_layout.addView(surface, surfaceCount);
m_surfaces.put(id, surface);
if (!m_splashScreenSticky) if (!m_splashScreenSticky)
hideSplashScreen(); hideSplashScreen();
}); });
} }
@UsedFromNativeCode @UsedFromNativeCode
public void setSurfaceGeometry(int id, int x, int y, int w, int h) { public void removeTopLevelWindow(final int id)
QtNative.runAction(() -> { {
if (m_surfaces.containsKey(id)) { QtNative.runAction(()-> {
QtSurface surface = m_surfaces.get(id); if (m_topLevelWindows.containsKey(id)) {
if (surface != null) QtWindow window = m_topLevelWindows.remove(id);
surface.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y)); if (m_topLevelWindows.isEmpty()) {
else // Keep last frame in stack until it is replaced to get correct
Log.e(QtNative.QtTAG, "setSurfaceGeometry(): surface is null!"); // shutdown transition
} else if (m_nativeViews.containsKey(id)) { m_dummyView = window;
View view = m_nativeViews.get(id); } else {
if (view != null) m_layout.removeView(window);
view.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y)); }
else
Log.e(QtNative.QtTAG, "setSurfaceGeometry(): view is null!");
} else {
Log.e(QtNative.QtTAG, "Surface " + id + " not found!");
} }
}); });
} }
@UsedFromNativeCode @UsedFromNativeCode
public void destroySurface(int id) { public void bringChildToFront(final int id)
QtNative.runAction(() -> {
View view = null;
if (m_surfaces.containsKey(id)) {
view = m_surfaces.remove(id);
} else if (m_nativeViews.containsKey(id)) {
view = m_nativeViews.remove(id);
} else {
Log.e(QtNative.QtTAG, "Surface " + id + " not found!");
}
if (view == null)
return;
// Keep last frame in stack until it is replaced to get correct
// shutdown transition
if (m_surfaces.size() == 0 && m_nativeViews.size() == 0) {
m_dummyView = view;
} else {
m_layout.removeView(view);
}
});
}
public int getSurfaceCount()
{
return m_surfaces.size();
}
@UsedFromNativeCode
public void bringChildToFront(int id)
{ {
QtNative.runAction(() -> { QtNative.runAction(() -> {
View view = m_surfaces.get(id); QtWindow window = m_topLevelWindows.get(id);
if (view != null) { if (window != null) {
final int surfaceCount = getSurfaceCount(); m_layout.moveChild(window, m_topLevelWindows.size() - 1);
if (surfaceCount > 0)
m_layout.moveChild(view, surfaceCount - 1);
return;
} }
view = m_nativeViews.get(id);
if (view != null)
m_layout.moveChild(view, -1);
}); });
} }
@ -513,17 +430,26 @@ class QtActivityDelegate
public void bringChildToBack(int id) public void bringChildToBack(int id)
{ {
QtNative.runAction(() -> { QtNative.runAction(() -> {
View view = m_surfaces.get(id); QtWindow window = m_topLevelWindows.get(id);
if (view != null) { if (window != null)
m_layout.moveChild(view, 0); m_layout.moveChild(window, 0);
return;
}
view = m_nativeViews.get(id);
if (view != null) {
final int index = getSurfaceCount();
m_layout.moveChild(view, index);
}
}); });
} }
private void setActivityBackgroundDrawable()
{
TypedValue attr = new TypedValue();
m_activity.getTheme().resolveAttribute(android.R.attr.windowBackground,
attr, true);
Drawable backgroundDrawable;
if (attr.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
attr.type <= TypedValue.TYPE_LAST_COLOR_INT) {
backgroundDrawable = new ColorDrawable(attr.data);
} else {
backgroundDrawable = m_activity.getResources().
getDrawable(attr.resourceId, m_activity.getTheme());
}
m_activity.getWindow().setBackgroundDrawable(backgroundDrawable);
}
} }

View File

@ -9,6 +9,7 @@ import android.content.Context;
import android.graphics.PixelFormat; import android.graphics.PixelFormat;
import android.view.GestureDetector; import android.view.GestureDetector;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.Surface;
import android.view.SurfaceHolder; import android.view.SurfaceHolder;
import android.view.SurfaceView; import android.view.SurfaceView;
@ -16,24 +17,33 @@ import android.view.SurfaceView;
class QtSurface extends SurfaceView implements SurfaceHolder.Callback class QtSurface extends SurfaceView implements SurfaceHolder.Callback
{ {
private final GestureDetector m_gestureDetector; private final GestureDetector m_gestureDetector;
private Object m_accessibilityDelegate = null;
private SurfaceChangedCallback m_surfaceCallback;
private final int m_windowId;
public QtSurface(Context context, int id, boolean onTop, int imageDepth) interface SurfaceChangedCallback {
void onSurfaceChanged(Surface surface);
}
public QtSurface(Context context, SurfaceChangedCallback surfaceCallback, int id, boolean onTop,
int imageDepth)
{ {
super(context); super(context);
setFocusable(false); setFocusable(false);
setFocusableInTouchMode(false); setFocusableInTouchMode(false);
setZOrderMediaOverlay(onTop); setZOrderMediaOverlay(onTop);
m_surfaceCallback = surfaceCallback;
getHolder().addCallback(this); getHolder().addCallback(this);
if (imageDepth == 16) if (imageDepth == 16)
getHolder().setFormat(PixelFormat.RGB_565); getHolder().setFormat(PixelFormat.RGB_565);
else else
getHolder().setFormat(PixelFormat.RGBA_8888); getHolder().setFormat(PixelFormat.RGBA_8888);
setId(id); m_windowId = id;
m_gestureDetector = m_gestureDetector =
new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
public void onLongPress(MotionEvent event) { public void onLongPress(MotionEvent event) {
QtInputDelegate.longPress(getId(), (int) event.getX(), (int) event.getY()); QtInputDelegate.longPress(m_windowId, (int) event.getX(), (int) event.getY());
} }
}); });
m_gestureDetector.setIsLongpressEnabled(true); m_gestureDetector.setIsLongpressEnabled(true);
@ -49,14 +59,15 @@ class QtSurface extends SurfaceView implements SurfaceHolder.Callback
{ {
if (width < 1 || height < 1) if (width < 1 || height < 1)
return; return;
if (m_surfaceCallback != null)
QtNative.setSurface(getId(), holder.getSurface()); m_surfaceCallback.onSurfaceChanged(holder.getSurface());
} }
@Override @Override
public void surfaceDestroyed(SurfaceHolder holder) public void surfaceDestroyed(SurfaceHolder holder)
{ {
QtNative.setSurface(getId(), null); if (m_surfaceCallback != null)
m_surfaceCallback.onSurfaceChanged(null);
} }
@Override @Override
@ -67,7 +78,7 @@ class QtSurface extends SurfaceView implements SurfaceHolder.Callback
// In case when Surface is moved, we should also add this move to event position // In case when Surface is moved, we should also add this move to event position
event.setLocation(event.getX() + getX(), event.getY() + getY()); event.setLocation(event.getX() + getX(), event.getY() + getY());
QtInputDelegate.sendTouchEvent(event, getId()); QtInputDelegate.sendTouchEvent(event, m_windowId);
m_gestureDetector.onTouchEvent(event); m_gestureDetector.onTouchEvent(event);
return true; return true;
} }
@ -75,13 +86,13 @@ class QtSurface extends SurfaceView implements SurfaceHolder.Callback
@Override @Override
public boolean onTrackballEvent(MotionEvent event) public boolean onTrackballEvent(MotionEvent event)
{ {
QtInputDelegate.sendTrackballEvent(event, getId()); QtInputDelegate.sendTrackballEvent(event, m_windowId);
return true; return true;
} }
@Override @Override
public boolean onGenericMotionEvent(MotionEvent event) public boolean onGenericMotionEvent(MotionEvent event)
{ {
return QtInputDelegate.sendGenericMotionEvent(event, getId()); return QtInputDelegate.sendGenericMotionEvent(event, m_windowId);
} }
} }

View File

@ -0,0 +1,114 @@
// Copyright (C) 2023 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.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
import java.util.HashMap;
public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallback {
private final static String TAG = "QtWindow";
private QtSurface m_surface;
private View m_nativeView;
private Handler m_androidHandler;
private static native void setSurface(int windowId, Surface surface);
public QtWindow(Context context)
{
super(context);
setId(View.generateViewId());
}
@Override
public void onSurfaceChanged(Surface surface)
{
setSurface(getId(), surface);
}
public void createSurface(final boolean onTop,
final int x, final int y, final int w, final int h,
final int imageDepth)
{
QtNative.runAction(new Runnable() {
@Override
public void run() {
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));
addView(surface, 0);
m_surface = surface;
}
});
}
public void destroySurface()
{
QtNative.runAction(new Runnable() {
@Override
public void run() {
if (m_surface != null) {
removeView(m_surface);
m_surface = null;
}
}
});
}
public void setSurfaceGeometry(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);
}
});
}
public void setNativeView(final View view,
final int x, final int y, final int w, final int h)
{
QtNative.runAction(new Runnable() {
@Override
public void run() {
if (m_nativeView != null)
removeView(m_nativeView);
m_nativeView = view;
m_nativeView.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
addView(m_nativeView);
}
});
}
public void removeNativeView()
{
QtNative.runAction(new Runnable() {
@Override
public void run() {
if (m_nativeView != null) {
removeView(m_nativeView);
m_nativeView = null;
}
}
});
}
}

View File

@ -70,10 +70,6 @@ static void *m_mainLibraryHnd = nullptr;
static QList<QByteArray> m_applicationParams; static QList<QByteArray> m_applicationParams;
static sem_t m_exitSemaphore, m_terminateSemaphore; static sem_t m_exitSemaphore, m_terminateSemaphore;
QHash<int, QAndroidPlatformWindow *> m_surfaces;
Q_CONSTINIT static QBasicMutex m_surfacesMutex;
static QAndroidPlatformIntegration *m_androidPlatformIntegration = nullptr; static QAndroidPlatformIntegration *m_androidPlatformIntegration = nullptr;
@ -319,56 +315,6 @@ namespace QtAndroid
return manufacturer + u' ' + model; return manufacturer + u' ' + model;
} }
jint generateViewId()
{
return QJniObject::callStaticMethod<jint>("android/view/View", "generateViewId", "()I");
}
int createSurface(QAndroidPlatformWindow *window, const QRect &geometry, bool onTop, int imageDepth)
{
QJniEnvironment env;
if (!env.jniEnv())
return -1;
m_surfacesMutex.lock();
jint surfaceId = generateViewId();
m_surfaces[surfaceId] = window;
m_surfacesMutex.unlock();
jint x = 0, y = 0, w = -1, h = -1;
if (!geometry.isNull()) {
x = geometry.x();
y = geometry.y();
w = std::max(geometry.width(), 1);
h = std::max(geometry.height(), 1);
}
qtActivityDelegate().callMethod<void>("createSurface", surfaceId, jboolean(onTop),
x, y, w, h, imageDepth);
return surfaceId;
}
int insertNativeView(QtJniTypes::View view, const QRect &geometry)
{
m_surfacesMutex.lock();
jint surfaceId = generateViewId();
m_surfaces[surfaceId] = nullptr; // dummy
m_surfacesMutex.unlock();
jint x = 0, y = 0, w = -1, h = -1;
if (!geometry.isNull())
geometry.getRect(&x, &y, &w, &h);
qtActivityDelegate().callMethod<void>("insertNativeView",
surfaceId,
view,
x,
y,
qMax(w, 1),
qMax(h, 1));
return surfaceId;
}
void setViewVisibility(jobject view, bool visible) void setViewVisibility(jobject view, bool visible)
{ {
QJniObject::callStaticMethod<void>(m_applicationClass, QJniObject::callStaticMethod<void>(m_applicationClass,
@ -378,56 +324,6 @@ namespace QtAndroid
visible); visible);
} }
void setSurfaceGeometry(int surfaceId, const QRect &geometry)
{
if (surfaceId == -1)
return;
QJniEnvironment env;
if (!env.jniEnv())
return;
jint x = 0, y = 0, w = -1, h = -1;
if (!geometry.isNull()) {
x = geometry.x();
y = geometry.y();
w = geometry.width();
h = geometry.height();
}
qtActivityDelegate().callMethod<void>("setSurfaceGeometry", surfaceId, x, y, w, h);
}
void destroySurface(int surfaceId)
{
if (surfaceId == -1)
return;
{
QMutexLocker lock(&m_surfacesMutex);
const auto &it = m_surfaces.find(surfaceId);
if (it != m_surfaces.end())
m_surfaces.erase(it);
}
qtActivityDelegate().callMethod<void>("destroySurface", surfaceId);
}
void bringChildToFront(int surfaceId)
{
if (surfaceId == -1)
return;
qtActivityDelegate().callMethod<void>("bringChildToFront", surfaceId);
}
void bringChildToBack(int surfaceId)
{
if (surfaceId == -1)
return;
qtActivityDelegate().callMethod<void>("bringChildToBack", surfaceId);
}
bool blockEventLoopsWhenSuspended() bool blockEventLoopsWhenSuspended()
{ {
static bool block = qEnvironmentVariableIntValue("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED"); static bool block = qEnvironmentVariableIntValue("QT_BLOCK_EVENT_LOOPS_WHEN_SUSPENDED");
@ -595,21 +491,6 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
sem_post(&m_exitSemaphore); sem_post(&m_exitSemaphore);
} }
static void setSurface(JNIEnv *env, jobject thiz, jint id, jobject jSurface)
{
Q_UNUSED(env);
Q_UNUSED(thiz);
QMutexLocker lock(&m_surfacesMutex);
const auto &it = m_surfaces.find(id);
if (it == m_surfaces.end())
return;
auto surfaceClient = it.value();
if (surfaceClient)
surfaceClient->onSurfaceChanged(jSurface);
}
static void setDisplayMetrics(JNIEnv * /*env*/, jclass /*clazz*/, jint screenWidthPixels, static void setDisplayMetrics(JNIEnv * /*env*/, jclass /*clazz*/, jint screenWidthPixels,
jint screenHeightPixels, jint availableLeftPixels, jint screenHeightPixels, jint availableLeftPixels,
jint availableTopPixels, jint availableWidthPixels, jint availableTopPixels, jint availableWidthPixels,
@ -802,7 +683,6 @@ static JNINativeMethod methods[] = {
{ "quitQtCoreApplication", "()V", (void *)quitQtCoreApplication }, { "quitQtCoreApplication", "()V", (void *)quitQtCoreApplication },
{ "terminateQt", "()V", (void *)terminateQt }, { "terminateQt", "()V", (void *)terminateQt },
{ "waitForServiceSetup", "()V", (void *)waitForServiceSetup }, { "waitForServiceSetup", "()V", (void *)waitForServiceSetup },
{ "setSurface", "(ILjava/lang/Object;)V", (void *)setSurface },
{ "updateWindow", "()V", (void *)updateWindow }, { "updateWindow", "()V", (void *)updateWindow },
{ "updateApplicationState", "(I)V", (void *)updateApplicationState }, { "updateApplicationState", "(I)V", (void *)updateApplicationState },
{ "onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult }, { "onActivityResult", "(IILandroid/content/Intent;)V", (void *)onActivityResult },
@ -948,7 +828,8 @@ Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void */*reserved*/)
|| !QtAndroidMenu::registerNatives(env) || !QtAndroidMenu::registerNatives(env)
|| !QtAndroidAccessibility::registerNatives(env) || !QtAndroidAccessibility::registerNatives(env)
|| !QtAndroidDialogHelpers::registerNatives(env) || !QtAndroidDialogHelpers::registerNatives(env)
|| !QAndroidPlatformClipboard::registerNatives(env)) { || !QAndroidPlatformClipboard::registerNatives(env)
|| !QAndroidPlatformWindow::registerNatives(env)) {
__android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed"); __android_log_print(ANDROID_LOG_FATAL, "Qt", "registerNatives failed");
return -1; return -1;
} }

View File

@ -28,7 +28,6 @@ class QBasicMutex;
Q_DECLARE_JNI_CLASS(QtActivityDelegate, "org/qtproject/qt/android/QtActivityDelegate") Q_DECLARE_JNI_CLASS(QtActivityDelegate, "org/qtproject/qt/android/QtActivityDelegate")
Q_DECLARE_JNI_CLASS(QtInputDelegate, "org/qtproject/qt/android/QtInputDelegate") Q_DECLARE_JNI_CLASS(QtInputDelegate, "org/qtproject/qt/android/QtInputDelegate")
Q_DECLARE_JNI_CLASS(View, "android/view/View");
namespace QtAndroid namespace QtAndroid
{ {
@ -36,14 +35,7 @@ namespace QtAndroid
QAndroidPlatformIntegration *androidPlatformIntegration(); QAndroidPlatformIntegration *androidPlatformIntegration();
void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration); void setAndroidPlatformIntegration(QAndroidPlatformIntegration *androidPlatformIntegration);
void setQtThread(QThread *thread); void setQtThread(QThread *thread);
int createSurface(QAndroidPlatformWindow *window, const QRect &geometry, bool onTop, int imageDepth);
int insertNativeView(QtJniTypes::View view, const QRect &geometry);
void setViewVisibility(jobject view, bool visible); void setViewVisibility(jobject view, bool visible);
void setSurfaceGeometry(int surfaceId, const QRect &geometry);
void destroySurface(int surfaceId);
void bringChildToFront(int surfaceId);
void bringChildToBack(int surfaceId);
QWindow *topLevelWindowAt(const QPoint &globalPos); QWindow *topLevelWindowAt(const QPoint &globalPos);
int availableWidthPixels(); int availableWidthPixels();

View File

@ -6,12 +6,12 @@
#include <QtCore/qvariant.h> #include <QtCore/qvariant.h>
#include <qpa/qwindowsysteminterface.h> #include <qpa/qwindowsysteminterface.h>
#include <QtCore/private/qjnihelpers_p.h> #include <QtCore/private/qjnihelpers_p.h>
#include <QtCore/qjnitypes.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
QAndroidPlatformForeignWindow::QAndroidPlatformForeignWindow(QWindow *window, WId nativeHandle) QAndroidPlatformForeignWindow::QAndroidPlatformForeignWindow(QWindow *window, WId nativeHandle)
: QAndroidPlatformWindow(window), : QAndroidPlatformWindow(window), m_view(nullptr), m_nativeViewInserted(false)
m_surfaceId(-1)
{ {
m_view = reinterpret_cast<jobject>(nativeHandle); m_view = reinterpret_cast<jobject>(nativeHandle);
if (m_view.isValid()) if (m_view.isValid())
@ -22,34 +22,17 @@ QAndroidPlatformForeignWindow::~QAndroidPlatformForeignWindow()
{ {
if (m_view.isValid()) if (m_view.isValid())
QtAndroid::setViewVisibility(m_view.object(), false); QtAndroid::setViewVisibility(m_view.object(), false);
if (m_surfaceId != -1)
QtAndroid::destroySurface(m_surfaceId);
}
void QAndroidPlatformForeignWindow::lower() m_nativeQtWindow.callMethod<void>("removeNativeView");
{
if (m_surfaceId == -1)
return;
QAndroidPlatformWindow::lower();
QtAndroid::bringChildToBack(m_surfaceId);
}
void QAndroidPlatformForeignWindow::raise()
{
if (m_surfaceId == -1)
return;
QAndroidPlatformWindow::raise();
QtAndroid::bringChildToFront(m_surfaceId);
} }
void QAndroidPlatformForeignWindow::setGeometry(const QRect &rect) void QAndroidPlatformForeignWindow::setGeometry(const QRect &rect)
{ {
QAndroidPlatformWindow::setGeometry(rect); QAndroidPlatformWindow::setGeometry(rect);
if (m_surfaceId != -1) if (m_nativeViewInserted)
QtAndroid::setSurfaceGeometry(m_surfaceId, rect); setSurfaceGeometry(rect);
} }
void QAndroidPlatformForeignWindow::setVisible(bool visible) void QAndroidPlatformForeignWindow::setVisible(bool visible)
@ -60,22 +43,21 @@ void QAndroidPlatformForeignWindow::setVisible(bool visible)
QtAndroid::setViewVisibility(m_view.object(), visible); QtAndroid::setViewVisibility(m_view.object(), visible);
QAndroidPlatformWindow::setVisible(visible); QAndroidPlatformWindow::setVisible(visible);
if (!visible && m_surfaceId != -1) { if (!visible && m_nativeViewInserted) {
QtAndroid::destroySurface(m_surfaceId); m_nativeQtWindow.callMethod<void>("removeNativeView");
m_surfaceId = -1; m_nativeViewInserted = false;
} else if (m_surfaceId == -1) { } else if (!m_nativeViewInserted) {
m_surfaceId = QtAndroid::insertNativeView(m_view.object(), geometry()); addViewToWindow();
} }
} }
void QAndroidPlatformForeignWindow::applicationStateChanged(Qt::ApplicationState state) void QAndroidPlatformForeignWindow::applicationStateChanged(Qt::ApplicationState state)
{ {
if (state <= Qt::ApplicationHidden if (state <= Qt::ApplicationHidden && m_nativeViewInserted) {
&& m_surfaceId != -1) { m_nativeQtWindow.callMethod<void>("removeNativeView");
QtAndroid::destroySurface(m_surfaceId); m_nativeViewInserted = false;
m_surfaceId = -1; } else if (m_view.isValid() && !m_nativeViewInserted){
} else if (m_view.isValid() && m_surfaceId == -1){ addViewToWindow();
m_surfaceId = QtAndroid::insertNativeView(m_view.object(), geometry());
} }
QAndroidPlatformWindow::applicationStateChanged(state); QAndroidPlatformWindow::applicationStateChanged(state);
@ -86,4 +68,14 @@ void QAndroidPlatformForeignWindow::setParent(const QPlatformWindow *window)
Q_UNUSED(window); Q_UNUSED(window);
} }
void QAndroidPlatformForeignWindow::addViewToWindow()
{
jint x = 0, y = 0, w = -1, h = -1;
if (!geometry().isNull())
geometry().getRect(&x, &y, &w, &h);
m_nativeQtWindow.callMethod<void>("setNativeView", m_view, x, y, qMax(w, 1), qMax(h, 1));
m_nativeViewInserted = true;
}
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -10,13 +10,13 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
Q_DECLARE_JNI_CLASS(View, "android/view/View")
class QAndroidPlatformForeignWindow : public QAndroidPlatformWindow class QAndroidPlatformForeignWindow : public QAndroidPlatformWindow
{ {
public: public:
explicit QAndroidPlatformForeignWindow(QWindow *window, WId nativeHandle); explicit QAndroidPlatformForeignWindow(QWindow *window, WId nativeHandle);
~QAndroidPlatformForeignWindow(); ~QAndroidPlatformForeignWindow();
void lower() override;
void raise() override;
void setGeometry(const QRect &rect) override; void setGeometry(const QRect &rect) override;
void setVisible(bool visible) override; void setVisible(bool visible) override;
void applicationStateChanged(Qt::ApplicationState state) override; void applicationStateChanged(Qt::ApplicationState state) override;
@ -24,8 +24,10 @@ public:
bool isForeignWindow() const override { return true; } bool isForeignWindow() const override { return true; }
private: private:
int m_surfaceId; void addViewToWindow();
QJniObject m_view;
QtJniTypes::View m_view;
bool m_nativeViewInserted;
}; };
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -58,12 +58,14 @@ void QAndroidPlatformOpenGLWindow::setGeometry(const QRect &rect)
EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config) EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config)
{ {
if (QAndroidEventDispatcherStopper::stopped() || QGuiApplication::applicationState() == Qt::ApplicationSuspended) if (QAndroidEventDispatcherStopper::stopped() ||
QGuiApplication::applicationState() == Qt::ApplicationSuspended || !window()->isTopLevel()) {
return m_eglSurface; return m_eglSurface;
}
QMutexLocker lock(&m_surfaceMutex); QMutexLocker lock(&m_surfaceMutex);
if (m_nativeSurfaceId == -1) { if (!m_surfaceCreated) {
AndroidDeadlockProtector protector; AndroidDeadlockProtector protector;
if (!protector.acquire()) if (!protector.acquire())
return m_eglSurface; return m_eglSurface;
@ -83,7 +85,7 @@ EGLSurface QAndroidPlatformOpenGLWindow::eglSurface(EGLConfig config)
bool QAndroidPlatformOpenGLWindow::checkNativeSurface(EGLConfig config) bool QAndroidPlatformOpenGLWindow::checkNativeSurface(EGLConfig config)
{ {
QMutexLocker lock(&m_surfaceMutex); QMutexLocker lock(&m_surfaceMutex);
if (m_nativeSurfaceId == -1 || !m_androidSurfaceObject.isValid()) if (!m_surfaceCreated || !m_androidSurfaceObject.isValid())
return false; // makeCurrent is NOT needed. return false; // makeCurrent is NOT needed.
createEgl(config); createEgl(config);

View File

@ -132,16 +132,16 @@ QAndroidPlatformScreen::~QAndroidPlatformScreen()
{ {
} }
QWindow *QAndroidPlatformScreen::topWindow() const QWindow *QAndroidPlatformScreen::topVisibleWindow() const
{ {
for (QAndroidPlatformWindow *w : m_windowStack) { for (QAndroidPlatformWindow *w : m_windowStack) {
if (w->window()->type() == Qt::Window || Qt::WindowType type = w->window()->type();
w->window()->type() == Qt::Popup || if (w->window()->isVisible() &&
w->window()->type() == Qt::Dialog) { (type == Qt::Window || type == Qt::Popup || type == Qt::Dialog)) {
return w->window(); return w->window();
} }
} }
return 0; return nullptr;
} }
QWindow *QAndroidPlatformScreen::topLevelAt(const QPoint &p) const QWindow *QAndroidPlatformScreen::topLevelAt(const QPoint &p) const
@ -162,37 +162,34 @@ void QAndroidPlatformScreen::addWindow(QAndroidPlatformWindow *window)
return; return;
m_windowStack.prepend(window); m_windowStack.prepend(window);
QtAndroid::qtActivityDelegate().callMethod<void>("addTopLevelWindow", window->nativeWindow());
QWindow *w = topWindow(); if (window->window()->isVisible())
QWindowSystemInterface::handleFocusWindowChanged(w, Qt::ActiveWindowFocusReason); topVisibleWindowChanged();
topWindowChanged(w);
} }
void QAndroidPlatformScreen::removeWindow(QAndroidPlatformWindow *window) void QAndroidPlatformScreen::removeWindow(QAndroidPlatformWindow *window)
{ {
if (window->parent() && window->isRaster())
return;
m_windowStack.removeOne(window); m_windowStack.removeOne(window);
if (m_windowStack.contains(window)) if (m_windowStack.contains(window))
qWarning() << "Failed to remove window"; qWarning() << "Failed to remove window";
QWindow *w = topWindow(); QtAndroid::qtActivityDelegate().callMethod<void>("removeTopLevelWindow", window->nativeViewId());
QWindowSystemInterface::handleFocusWindowChanged(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w); topVisibleWindowChanged();
} }
void QAndroidPlatformScreen::raise(QAndroidPlatformWindow *window) void QAndroidPlatformScreen::raise(QAndroidPlatformWindow *window)
{ {
int index = m_windowStack.indexOf(window); int index = m_windowStack.indexOf(window);
if (index <= 0) if (index < 0)
return; return;
m_windowStack.move(index, 0); if (index > 0) {
m_windowStack.move(index, 0);
QWindow *w = topWindow(); QtAndroid::qtActivityDelegate().callMethod<void>("bringChildToFront", window->nativeViewId());
QWindowSystemInterface::handleFocusWindowChanged(w, Qt::ActiveWindowFocusReason); }
topWindowChanged(w); topVisibleWindowChanged();
} }
void QAndroidPlatformScreen::lower(QAndroidPlatformWindow *window) void QAndroidPlatformScreen::lower(QAndroidPlatformWindow *window)
@ -201,10 +198,9 @@ void QAndroidPlatformScreen::lower(QAndroidPlatformWindow *window)
if (index == -1 || index == (m_windowStack.size() - 1)) if (index == -1 || index == (m_windowStack.size() - 1))
return; return;
m_windowStack.move(index, m_windowStack.size() - 1); m_windowStack.move(index, m_windowStack.size() - 1);
QtAndroid::qtActivityDelegate().callMethod<void>("bringChildToBack", window->nativeViewId());
QWindow *w = topWindow(); topVisibleWindowChanged();
QWindowSystemInterface::handleFocusWindowChanged(w, Qt::ActiveWindowFocusReason);
topWindowChanged(w);
} }
void QAndroidPlatformScreen::setPhysicalSize(const QSize &size) void QAndroidPlatformScreen::setPhysicalSize(const QSize &size)
@ -284,13 +280,14 @@ void QAndroidPlatformScreen::applicationStateChanged(Qt::ApplicationState state)
w->applicationStateChanged(state); w->applicationStateChanged(state);
} }
void QAndroidPlatformScreen::topWindowChanged(QWindow *w) void QAndroidPlatformScreen::topVisibleWindowChanged()
{ {
QWindow *w = topVisibleWindow();
QWindowSystemInterface::handleFocusWindowChanged(w, Qt::ActiveWindowFocusReason);
QtAndroidMenu::setActiveTopLevelWindow(w); QtAndroidMenu::setActiveTopLevelWindow(w);
if (w && w->handle()) {
if (w != 0) {
QAndroidPlatformWindow *platformWindow = static_cast<QAndroidPlatformWindow *>(w->handle()); QAndroidPlatformWindow *platformWindow = static_cast<QAndroidPlatformWindow *>(w->handle());
if (platformWindow != 0) if (platformWindow)
platformWindow->updateSystemUiVisibility(); platformWindow->updateSystemUiVisibility();
} }
} }

View File

@ -37,7 +37,7 @@ public:
int currentMode() const override { return m_currentMode; } int currentMode() const override { return m_currentMode; }
int preferredMode() const override { return m_currentMode; } int preferredMode() const override { return m_currentMode; }
qreal refreshRate() const override { return m_refreshRate; } qreal refreshRate() const override { return m_refreshRate; }
inline QWindow *topWindow() const; inline QWindow *topVisibleWindow() const;
QWindow *topLevelAt(const QPoint & p) const override; QWindow *topLevelAt(const QPoint & p) const override;
void addWindow(QAndroidPlatformWindow *window); void addWindow(QAndroidPlatformWindow *window);
@ -45,7 +45,7 @@ public:
void raise(QAndroidPlatformWindow *window); void raise(QAndroidPlatformWindow *window);
void lower(QAndroidPlatformWindow *window); void lower(QAndroidPlatformWindow *window);
void topWindowChanged(QWindow *w); void topVisibleWindowChanged();
int displayId() const override; int displayId() const override;
public slots: public slots:
@ -60,7 +60,6 @@ public slots:
protected: protected:
typedef QList<QAndroidPlatformWindow *> WindowStackType; typedef QList<QAndroidPlatformWindow *> WindowStackType;
WindowStackType m_windowStack; WindowStackType m_windowStack;
QRect m_availableGeometry; QRect m_availableGeometry;
int m_depth; int m_depth;
QImage::Format m_format; QImage::Format m_format;

View File

@ -94,7 +94,7 @@ VkSurfaceKHR *QAndroidPlatformVulkanWindow::vkSurface()
clearSurface(); clearSurface();
QMutexLocker lock(&m_surfaceMutex); QMutexLocker lock(&m_surfaceMutex);
if (m_nativeSurfaceId == -1) { if (!m_surfaceCreated) {
AndroidDeadlockProtector protector; AndroidDeadlockProtector protector;
if (!protector.acquire()) if (!protector.acquire())
return &m_vkSurface; return &m_vkSurface;
@ -102,7 +102,7 @@ VkSurfaceKHR *QAndroidPlatformVulkanWindow::vkSurface()
m_surfaceWaitCondition.wait(&m_surfaceMutex); m_surfaceWaitCondition.wait(&m_surfaceMutex);
} }
if (m_nativeSurfaceId == -1 || !m_androidSurfaceObject.isValid()) if (!m_surfaceCreated || !m_androidSurfaceObject.isValid())
return &m_vkSurface; return &m_vkSurface;
QJniEnvironment env; QJniEnvironment env;

View File

@ -14,11 +14,15 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
Q_LOGGING_CATEGORY(lcQpaWindow, "qt.qpa.window")
Q_CONSTINIT static QBasicAtomicInt winIdGenerator = Q_BASIC_ATOMIC_INITIALIZER(0); Q_CONSTINIT static QBasicAtomicInt winIdGenerator = Q_BASIC_ATOMIC_INITIALIZER(0);
QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window) QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
: QPlatformWindow(window), m_androidSurfaceObject(nullptr) : QPlatformWindow(window), m_nativeQtWindow(QNativeInterface::QAndroidApplication::context()),
m_androidSurfaceObject(nullptr)
{ {
m_nativeViewId = m_nativeQtWindow.callMethod<jint>("getId");
m_windowFlags = Qt::Widget; m_windowFlags = Qt::Widget;
m_windowState = Qt::WindowNoState; m_windowState = Qt::WindowNoState;
// the surfaceType is overwritten in QAndroidPlatformOpenGLWindow ctor so let's save // the surfaceType is overwritten in QAndroidPlatformOpenGLWindow ctor so let's save
@ -44,8 +48,15 @@ QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
if (requestedNativeGeometry != finalNativeGeometry) if (requestedNativeGeometry != finalNativeGeometry)
setGeometry(finalNativeGeometry); setGeometry(finalNativeGeometry);
} }
platformScreen()->addWindow(this);
} }
QAndroidPlatformWindow::~QAndroidPlatformWindow()
{
platformScreen()->removeWindow(this);
}
void QAndroidPlatformWindow::lower() void QAndroidPlatformWindow::lower()
{ {
platformScreen()->lower(this); platformScreen()->lower(this);
@ -76,23 +87,19 @@ void QAndroidPlatformWindow::setGeometry(const QRect &rect)
void QAndroidPlatformWindow::setVisible(bool visible) void QAndroidPlatformWindow::setVisible(bool visible)
{ {
if (visible)
updateSystemUiVisibility();
if (visible) { if (visible) {
updateSystemUiVisibility();
if ((m_windowState & Qt::WindowFullScreen) if ((m_windowState & Qt::WindowFullScreen)
|| ((m_windowState & Qt::WindowMaximized) && (window()->flags() & Qt::MaximizeUsingFullscreenGeometryHint))) { || ((m_windowState & Qt::WindowMaximized) && (window()->flags() & Qt::MaximizeUsingFullscreenGeometryHint))) {
setGeometry(platformScreen()->geometry()); setGeometry(platformScreen()->geometry());
} else if (m_windowState & Qt::WindowMaximized) { } else if (m_windowState & Qt::WindowMaximized) {
setGeometry(platformScreen()->availableGeometry()); setGeometry(platformScreen()->availableGeometry());
} }
requestActivateWindow();
} else if (window() == qGuiApp->focusWindow()) {
platformScreen()->topVisibleWindowChanged();
} }
if (visible)
platformScreen()->addWindow(this);
else
platformScreen()->removeWindow(this);
QRect availableGeometry = screen()->availableGeometry(); QRect availableGeometry = screen()->availableGeometry();
if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0) if (geometry().width() > 0 && geometry().height() > 0 && availableGeometry.width() > 0 && availableGeometry.height() > 0)
QPlatformWindow::setVisible(visible); QPlatformWindow::setVisible(visible);
@ -125,7 +132,14 @@ Qt::WindowFlags QAndroidPlatformWindow::windowFlags() const
void QAndroidPlatformWindow::setParent(const QPlatformWindow *window) void QAndroidPlatformWindow::setParent(const QPlatformWindow *window)
{ {
Q_UNUSED(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
if (window) {
platformScreen()->removeWindow(this);
} else {
platformScreen()->addWindow(this);
}
} }
QAndroidPlatformScreen *QAndroidPlatformWindow::platformScreen() const QAndroidPlatformScreen *QAndroidPlatformWindow::platformScreen() const
@ -140,7 +154,8 @@ void QAndroidPlatformWindow::propagateSizeHints()
void QAndroidPlatformWindow::requestActivateWindow() void QAndroidPlatformWindow::requestActivateWindow()
{ {
platformScreen()->topWindowChanged(window()); if (!blockedByModal())
raise();
} }
void QAndroidPlatformWindow::updateSystemUiVisibility() void QAndroidPlatformWindow::updateSystemUiVisibility()
@ -176,25 +191,62 @@ void QAndroidPlatformWindow::applicationStateChanged(Qt::ApplicationState)
void QAndroidPlatformWindow::createSurface() void QAndroidPlatformWindow::createSurface()
{ {
const QRect rect = geometry();
jint x = 0, y = 0, w = -1, h = -1;
if (!rect.isNull()) {
x = rect.x();
y = rect.y();
w = std::max(rect.width(), 1);
h = std::max(rect.height(), 1);
}
const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint); const bool windowStaysOnTop = bool(window()->flags() & Qt::WindowStaysOnTopHint);
m_nativeSurfaceId = QtAndroid::createSurface(this, geometry(), windowStaysOnTop, 32);
m_nativeQtWindow.callMethod<void>("createSurface", windowStaysOnTop, x, y, w, h, 32);
m_surfaceCreated = true;
} }
void QAndroidPlatformWindow::destroySurface() void QAndroidPlatformWindow::destroySurface()
{ {
if (m_nativeSurfaceId != -1) { if (m_surfaceCreated) {
QtAndroid::destroySurface(m_nativeSurfaceId); m_nativeQtWindow.callMethod<void>("destroySurface");
m_nativeSurfaceId = -1; m_surfaceCreated = false;
} }
} }
void QAndroidPlatformWindow::setSurfaceGeometry(const QRect &rect) void QAndroidPlatformWindow::setSurfaceGeometry(const QRect &geometry)
{ {
if (m_nativeSurfaceId != -1) if (!m_surfaceCreated)
QtAndroid::setSurfaceGeometry(m_nativeSurfaceId, rect); return;
jint x = 0;
jint y = 0;
jint w = -1;
jint h = -1;
if (!geometry.isNull()) {
x = geometry.x();
y = geometry.y();
w = geometry.width();
h = geometry.height();
}
m_nativeQtWindow.callMethod<void>("setSurfaceGeometry", x, y, w, h);
} }
void QAndroidPlatformWindow::sendExpose() void QAndroidPlatformWindow::onSurfaceChanged(QtJniTypes::Surface surface)
{
lockSurface();
m_androidSurfaceObject = surface;
if (m_androidSurfaceObject.isValid()) // wait until we have a valid surface to draw into
m_surfaceWaitCondition.wakeOne();
unlockSurface();
if (m_androidSurfaceObject.isValid()) {
// repaint the window, when we have a valid surface
sendExpose();
}
}
void QAndroidPlatformWindow::sendExpose() const
{ {
QRect availableGeometry = screen()->availableGeometry(); QRect availableGeometry = screen()->availableGeometry();
if (!geometry().isNull() && !availableGeometry.isNull()) { if (!geometry().isNull() && !availableGeometry.isNull()) {
@ -203,15 +255,41 @@ void QAndroidPlatformWindow::sendExpose()
} }
} }
void QAndroidPlatformWindow::onSurfaceChanged(QtJniTypes::Surface surface) bool QAndroidPlatformWindow::blockedByModal() const
{ {
lockSurface(); QWindow *modalWindow = QGuiApplication::modalWindow();
m_androidSurfaceObject = surface; return modalWindow && modalWindow != window();
if (m_androidSurfaceObject.isValid()) }
m_surfaceWaitCondition.wakeOne();
unlockSurface(); void QAndroidPlatformWindow::setSurface(JNIEnv *env, jobject object, jint windowId,
if (m_androidSurfaceObject.isValid()) QtJniTypes::Surface surface)
sendExpose(); {
Q_UNUSED(env)
Q_UNUSED(object)
if (!qGuiApp)
return;
const QList<QWindow*> windows = qGuiApp->allWindows();
for (QWindow * window : windows) {
if (!window->handle())
continue;
QAndroidPlatformWindow *platformWindow =
static_cast<QAndroidPlatformWindow *>(window->handle());
if (platformWindow->nativeViewId() == windowId)
platformWindow->onSurfaceChanged(surface);
}
}
bool QAndroidPlatformWindow::registerNatives(QJniEnvironment &env)
{
if (!env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtWindow>::className(),
{Q_JNI_NATIVE_SCOPED_METHOD(setSurface, QAndroidPlatformWindow)})) {
qCCritical(lcQpaWindow) << "RegisterNatives failed for"
<< QtJniTypes::Traits<QtJniTypes::QtWindow>::className();
return false;
}
return true;
} }
QT_END_NAMESPACE QT_END_NAMESPACE

View File

@ -17,6 +17,8 @@
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(lcQpaWindow)
Q_DECLARE_JNI_CLASS(QtWindow, "org/qtproject/qt/android/QtWindow")
Q_DECLARE_JNI_CLASS(Surface, "android/view/Surface") Q_DECLARE_JNI_CLASS(Surface, "android/view/Surface")
class QAndroidPlatformScreen; class QAndroidPlatformScreen;
@ -25,7 +27,7 @@ class QAndroidPlatformWindow: public QPlatformWindow
{ {
public: public:
explicit QAndroidPlatformWindow(QWindow *window); explicit QAndroidPlatformWindow(QWindow *window);
~QAndroidPlatformWindow();
void lower() override; void lower() override;
void raise() override; void raise() override;
@ -49,9 +51,12 @@ public:
void updateSystemUiVisibility(); void updateSystemUiVisibility();
inline bool isRaster() const { return m_isRaster; } inline bool isRaster() const { return m_isRaster; }
bool isExposed() const override; bool isExposed() const override;
QtJniTypes::QtWindow nativeWindow() const { return m_nativeQtWindow; }
virtual void applicationStateChanged(Qt::ApplicationState); virtual void applicationStateChanged(Qt::ApplicationState);
int nativeViewId() const { return m_nativeViewId; }
static bool registerNatives(QJniEnvironment &env);
void onSurfaceChanged(QtJniTypes::Surface surface); void onSurfaceChanged(QtJniTypes::Surface surface);
protected: protected:
@ -60,20 +65,27 @@ protected:
void unlockSurface() { m_surfaceMutex.unlock(); } void unlockSurface() { m_surfaceMutex.unlock(); }
void createSurface(); void createSurface();
void destroySurface(); void destroySurface();
void setSurfaceGeometry(const QRect &rect); void setSurfaceGeometry(const QRect &geometry);
void sendExpose(); void sendExpose() const;
bool blockedByModal() const;
protected:
Qt::WindowFlags m_windowFlags; Qt::WindowFlags m_windowFlags;
Qt::WindowStates m_windowState; Qt::WindowStates m_windowState;
bool m_isRaster; bool m_isRaster;
WId m_windowId; WId m_windowId;
QtJniTypes::QtWindow m_nativeQtWindow;
// The Android Surface, accessed from multiple threads, guarded by m_surfaceMutex // The Android Surface, accessed from multiple threads, guarded by m_surfaceMutex
QtJniTypes::Surface m_androidSurfaceObject; QtJniTypes::Surface m_androidSurfaceObject;
QWaitCondition m_surfaceWaitCondition; QWaitCondition m_surfaceWaitCondition;
int m_nativeSurfaceId = -1; int m_nativeViewId = -1;
bool m_surfaceCreated = false;
QMutex m_surfaceMutex; QMutex m_surfaceMutex;
private:
static void setSurface(JNIEnv *env, jobject obj, jint windowId, QtJniTypes::Surface surface);
Q_DECLARE_JNI_NATIVE_METHOD_IN_CURRENT_SCOPE(setSurface)
}; };
QT_END_NAMESPACE QT_END_NAMESPACE