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));
m_layout.addView(window, m_topLevelWindows.size());
m_layout.addView(window.getLayout(), m_topLevelWindows.size());
m_topLevelWindows.put(window.getId(), window);
if (!m_splashScreenSticky)
hideSplashScreen();
@ -406,9 +406,9 @@ class QtActivityDelegate
if (m_topLevelWindows.isEmpty()) {
// Keep last frame in stack until it is replaced to get correct
// shutdown transition
m_dummyView = window;
m_dummyView = window.getLayout();
} else {
m_layout.removeView(window);
m_layout.removeView(window.getLayout());
}
}
});
@ -420,7 +420,7 @@ class QtActivityDelegate
QtNative.runAction(() -> {
QtWindow window = m_topLevelWindows.get(id);
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(() -> {
QtWindow window = m_topLevelWindows.get(id);
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;
public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallback {
public class QtWindow implements QtSurface.SurfaceChangedCallback {
private final static String TAG = "QtWindow";
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 static native void setSurface(int windowId, Surface surface);
public QtWindow(Context context, QtWindow parentWindow)
{
super(context);
setId(View.generateViewId());
setParent(parentWindow);
m_id = View.generateViewId();
QtNative.runAction(() -> {
m_layout = new QtLayout(context);
setParent(parentWindow);
});
}
void setVisible(boolean visible) {
QtNative.runAction(() -> {
if (visible)
setVisibility(View.VISIBLE);
m_layout.setVisibility(View.VISIBLE);
else
setVisibility(View.INVISIBLE);
m_layout.setVisibility(View.INVISIBLE);
});
}
public int getId()
{
return m_id;
}
public QtLayout getLayout()
{
return m_layout;
}
@Override
public void onSurfaceChanged(Surface surface)
{
@ -58,15 +71,15 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
@Override
public void run() {
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
// now get created earlier than the parents -> they are behind the parent window
// without this, and SurfaceView z-ordering is limited
boolean tempOnTop = onTop || (m_parentWindow != null);
QtSurface surface = new QtSurface(getContext(), QtWindow.this,
QtSurface surface = new QtSurface(m_layout.getContext(), QtWindow.this,
QtWindow.this.getId(), tempOnTop, imageDepth);
surface.setLayoutParams(new QtLayout.LayoutParams(
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.
// All other views are stacked based on the order they are created.
addView(surface, 0);
m_layout.addView(surface, 0);
m_surface = surface;
}
});
@ -86,7 +99,7 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
@Override
public void run() {
if (m_surface != null) {
removeView(m_surface);
m_layout.removeView(m_surface);
m_surface = null;
}
}
@ -98,7 +111,7 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
QtNative.runAction(new Runnable() {
@Override
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
public void run() {
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
public void run() {
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
public void run() {
if (m_nativeView != null)
removeView(m_nativeView);
m_layout.removeView(m_nativeView);
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,
ViewGroup.LayoutParams.MATCH_PARENT));
addView(m_nativeView);
m_layout.addView(m_nativeView);
}
});
}
public void bringChildToFront(int id)
{
View view = m_childWindows.get(id);
if (view != null) {
if (getChildCount() > 0)
moveChild(view, getChildCount() - 1);
}
QtNative.runAction(()-> {
QtWindow window = m_childWindows.get(id);
if (window != null) {
if (m_layout.getChildCount() > 0)
m_layout.moveChild(window.getLayout(), m_layout.getChildCount() - 1);
}
});
}
public void bringChildToBack(int id) {
QtNative.runAction(new Runnable() {
@Override
public void run() {
View view = m_childWindows.get(id);
if (view != null) {
moveChild(view, 0);
QtWindow window = m_childWindows.get(id);
if (window != null) {
m_layout.moveChild(window.getLayout(), 0);
}
}
});
@ -170,7 +185,7 @@ public class QtWindow extends QtLayout implements QtSurface.SurfaceChangedCallba
@Override
public void run() {
if (m_nativeView != null) {
removeView(m_nativeView);
m_layout.removeView(m_nativeView);
m_nativeView = null;
}
}

View File

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