Android: Make QtWindow wrap the QtLayout instead of inherit it

The layout is a ViewGroup, and should be created in the Android UI
thread, while for ease of use on C++ side we should be able to
construct the Java object in the platform window constructor to
avoid later calls not having a valid object reference. Trying to
make a blocking call to Android thread from Qt thread can lead to
dead locks, so move only the creation of the layout itself into
Android thread, making the QtWindow a wrapper for it, which we can
immediately return to C++/Qt thread. Most of the calls made to
QtWindow are anyway already passed to the Android UI thread.

As a drive by, add a missing QtNative.runAction() to
bringChildToFront().

Change-Id: Ib2495ddda8267384656557cbe40be5da869f82c3
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
Tinja Paavoseppä 2023-12-08 11:24:43 +02:00
parent 6ff88f97a6
commit 2bc7d38bd6
3 changed files with 52 additions and 39 deletions

View File

@ -386,11 +386,11 @@ class QtActivityDelegate
} }
} }
window.setLayoutParams(new ViewGroup.LayoutParams( window.getLayout().setLayoutParams(new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT)); ViewGroup.LayoutParams.MATCH_PARENT));
m_layout.addView(window, m_topLevelWindows.size()); m_layout.addView(window.getLayout(), m_topLevelWindows.size());
m_topLevelWindows.put(window.getId(), window); m_topLevelWindows.put(window.getId(), window);
if (!m_splashScreenSticky) if (!m_splashScreenSticky)
hideSplashScreen(); hideSplashScreen();
@ -406,9 +406,9 @@ class QtActivityDelegate
if (m_topLevelWindows.isEmpty()) { if (m_topLevelWindows.isEmpty()) {
// Keep last frame in stack until it is replaced to get correct // Keep last frame in stack until it is replaced to get correct
// shutdown transition // shutdown transition
m_dummyView = window; m_dummyView = window.getLayout();
} else { } else {
m_layout.removeView(window); m_layout.removeView(window.getLayout());
} }
} }
}); });
@ -420,7 +420,7 @@ class QtActivityDelegate
QtNative.runAction(() -> { QtNative.runAction(() -> {
QtWindow window = m_topLevelWindows.get(id); QtWindow window = m_topLevelWindows.get(id);
if (window != null) { if (window != null) {
m_layout.moveChild(window, m_topLevelWindows.size() - 1); m_layout.moveChild(window.getLayout(), m_topLevelWindows.size() - 1);
} }
}); });
} }
@ -431,7 +431,7 @@ class QtActivityDelegate
QtNative.runAction(() -> { QtNative.runAction(() -> {
QtWindow window = m_topLevelWindows.get(id); QtWindow window = m_topLevelWindows.get(id);
if (window != null) if (window != null)
m_layout.moveChild(window, 0); m_layout.moveChild(window.getLayout(), 0);
}); });
} }

View File

@ -11,33 +11,46 @@ import android.view.ViewGroup;
import java.util.HashMap; import java.util.HashMap;
public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallback { public class QtWindow implements QtSurface.SurfaceChangedCallback {
private final static String TAG = "QtWindow"; private final static String TAG = "QtWindow";
private QtLayout m_layout;
private QtSurface m_surface; private QtSurface m_surface;
private View m_nativeView; private View m_nativeView;
private HashMap<Integer, QtWindow> m_childWindows = new HashMap<Integer, QtWindow>(); private HashMap<Integer, QtWindow> m_childWindows = new HashMap<Integer, QtWindow>();
private QtWindow m_parentWindow; private QtWindow m_parentWindow;
private int m_id;
private static native void setSurface(int windowId, Surface surface); private static native void setSurface(int windowId, Surface surface);
public QtWindow(Context context, QtWindow parentWindow) public QtWindow(Context context, QtWindow parentWindow)
{ {
super(context); m_id = View.generateViewId();
setId(View.generateViewId()); QtNative.runAction(() -> {
m_layout = new QtLayout(context);
setParent(parentWindow); setParent(parentWindow);
});
} }
void setVisible(boolean visible) { void setVisible(boolean visible) {
QtNative.runAction(() -> { QtNative.runAction(() -> {
if (visible) if (visible)
setVisibility(View.VISIBLE); m_layout.setVisibility(View.VISIBLE);
else else
setVisibility(View.INVISIBLE); m_layout.setVisibility(View.INVISIBLE);
}); });
} }
public int getId()
{
return m_id;
}
public QtLayout getLayout()
{
return m_layout;
}
@Override @Override
public void onSurfaceChanged(Surface surface) public void onSurfaceChanged(Surface surface)
{ {
@ -58,15 +71,15 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
@Override @Override
public void run() { public void run() {
if (m_surface != null) if (m_surface != null)
removeView(m_surface); m_layout.removeView(m_surface);
setLayoutParams(new QtLayout.LayoutParams(w, h, x, y)); m_layout.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
// TODO currently setting child windows to onTop, since their surfaces // TODO currently setting child windows to onTop, since their surfaces
// now get created earlier than the parents -> they are behind the parent window // now get created earlier than the parents -> they are behind the parent window
// without this, and SurfaceView z-ordering is limited // without this, and SurfaceView z-ordering is limited
boolean tempOnTop = onTop || (m_parentWindow != null); boolean tempOnTop = onTop || (m_parentWindow != null);
QtSurface surface = new QtSurface(getContext(), QtWindow.this, QtSurface surface = new QtSurface(m_layout.getContext(), QtWindow.this,
QtWindow.this.getId(), tempOnTop, imageDepth); QtWindow.this.getId(), tempOnTop, imageDepth);
surface.setLayoutParams(new QtLayout.LayoutParams( surface.setLayoutParams(new QtLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT,
@ -74,7 +87,7 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
// The QtSurface of this window will be added as the first of the stack. // 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. // All other views are stacked based on the order they are created.
addView(surface, 0); m_layout.addView(surface, 0);
m_surface = surface; m_surface = surface;
} }
}); });
@ -86,7 +99,7 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
@Override @Override
public void run() { public void run() {
if (m_surface != null) { if (m_surface != null) {
removeView(m_surface); m_layout.removeView(m_surface);
m_surface = null; m_surface = null;
} }
} }
@ -98,7 +111,7 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
QtNative.runAction(new Runnable() { QtNative.runAction(new Runnable() {
@Override @Override
public void run() { public void run() {
QtWindow.this.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y)); m_layout.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
} }
}); });
} }
@ -109,7 +122,7 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
@Override @Override
public void run() { public void run() {
m_childWindows.put(window.getId(), window); m_childWindows.put(window.getId(), window);
addView(window, getChildCount()); m_layout.addView(window.getLayout(), m_layout.getChildCount());
} }
}); });
} }
@ -120,7 +133,7 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
@Override @Override
public void run() { public void run() {
if (m_childWindows.containsKey(id)) if (m_childWindows.containsKey(id))
removeView(m_childWindows.remove(id)); m_layout.removeView(m_childWindows.remove(id).getLayout());
} }
}); });
} }
@ -132,33 +145,35 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
@Override @Override
public void run() { public void run() {
if (m_nativeView != null) if (m_nativeView != null)
removeView(m_nativeView); m_layout.removeView(m_nativeView);
m_nativeView = view; m_nativeView = view;
QtWindow.this.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y)); m_layout.setLayoutParams(new QtLayout.LayoutParams(w, h, x, y));
m_nativeView.setLayoutParams(new QtLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, m_nativeView.setLayoutParams(new QtLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT)); ViewGroup.LayoutParams.MATCH_PARENT));
addView(m_nativeView); m_layout.addView(m_nativeView);
} }
}); });
} }
public void bringChildToFront(int id) public void bringChildToFront(int id)
{ {
View view = m_childWindows.get(id); QtNative.runAction(()-> {
if (view != null) { QtWindow window = m_childWindows.get(id);
if (getChildCount() > 0) if (window != null) {
moveChild(view, getChildCount() - 1); if (m_layout.getChildCount() > 0)
} m_layout.moveChild(window.getLayout(), m_layout.getChildCount() - 1);
}
});
} }
public void bringChildToBack(int id) { public void bringChildToBack(int id) {
QtNative.runAction(new Runnable() { QtNative.runAction(new Runnable() {
@Override @Override
public void run() { public void run() {
View view = m_childWindows.get(id); QtWindow window = m_childWindows.get(id);
if (view != null) { if (window != null) {
moveChild(view, 0); m_layout.moveChild(window.getLayout(), 0);
} }
} }
}); });
@ -170,7 +185,7 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
@Override @Override
public void run() { public void run() {
if (m_nativeView != null) { if (m_nativeView != null) {
removeView(m_nativeView); m_layout.removeView(m_nativeView);
m_nativeView = null; m_nativeView = null;
} }
} }

View File

@ -51,12 +51,10 @@ QAndroidPlatformWindow::QAndroidPlatformWindow(QWindow *window)
if (parent()) if (parent())
m_nativeParentQtWindow = static_cast<QAndroidPlatformWindow*>(parent())->nativeWindow(); m_nativeParentQtWindow = static_cast<QAndroidPlatformWindow*>(parent())->nativeWindow();
QNativeInterface::QAndroidApplication::runOnAndroidMainThread([this]() { m_nativeQtWindow = QJniObject::construct<QtJniTypes::QtWindow>(
m_nativeQtWindow = QJniObject::construct<QtJniTypes::QtWindow>( QNativeInterface::QAndroidApplication::context(),
QNativeInterface::QAndroidApplication::context(), m_nativeParentQtWindow);
m_nativeParentQtWindow); m_nativeViewId = m_nativeQtWindow.callMethod<jint>("getId");
m_nativeViewId = m_nativeQtWindow.callMethod<jint>("getId");
}).waitForFinished();
if (window->isTopLevel()) if (window->isTopLevel())
platformScreen()->addWindow(this); platformScreen()->addWindow(this);