-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Android: Add classes to embed QML into native Android
Add classes that make it possible to add QML as a View into a native Android app: QtView: Base class for QtQuickView, handles non-Quick dependent operations. In essence a Java ViewGroup class which loads a QWindow and embeds it into itself. QtEmbeddedLoader: Extends QtLoader for embedded case, creates the embedded version of QtActivityDelegate and provides an embedded-specific path to loading Qt libraries (Mostly just allows users to set the name of the main lib) QtAndroidWindowEmbedding namespace: Deals with calls from QtEmbeddedDelegate to create/destroy QWindow and from QtView to show the window. Take the QtEmbeddedDelegate introduced in an earlier commit into use, and add functionality for loading QWindows for QtViews and managing QtViews into it. Add a factory for creating instances of QtEmbeddedDelegate. The factory holds a map of QtEmbeddedDelegate objects and creates them, with the Activity as the key. This is to make it so that the same delegate can be used by multiple views which share the same Context. Known issues left: * keyboard focus not working, as with other child windows Pick-to: 6.7 Task-number: QTBUG-118872 Change-Id: I94a5f9b4f904c05cc6368cf20f273fcf10d31f17 Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
- Loading branch information
Tinja Paavoseppä
committed
Jan 30, 2024
1 parent
b3441b4
commit 702c420
Showing
10 changed files
with
376 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
37 changes: 37 additions & 0 deletions
37
src/android/jar/src/org/qtproject/qt/android/QtEmbeddedDelegateFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
// Copyright (C) 2024 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.app.Activity; | ||
import android.app.Application; | ||
import android.os.Bundle; | ||
|
||
import java.util.HashMap; | ||
|
||
class QtEmbeddedDelegateFactory { | ||
private static final HashMap<Activity, QtEmbeddedDelegate> m_delegates = new HashMap<>(); | ||
private static final Object m_delegateLock = new Object(); | ||
|
||
@UsedFromNativeCode | ||
public static QtActivityDelegateBase getActivityDelegate(Activity activity) { | ||
synchronized (m_delegateLock) { | ||
return m_delegates.get(activity); | ||
} | ||
} | ||
|
||
public static QtEmbeddedDelegate create(Activity activity) { | ||
synchronized (m_delegateLock) { | ||
if (!m_delegates.containsKey(activity)) | ||
m_delegates.put(activity, new QtEmbeddedDelegate(activity)); | ||
|
||
return m_delegates.get(activity); | ||
} | ||
} | ||
|
||
public static void remove(Activity activity) { | ||
synchronized (m_delegateLock) { | ||
m_delegates.remove(activity); | ||
} | ||
} | ||
} |
48 changes: 48 additions & 0 deletions
48
src/android/jar/src/org/qtproject/qt/android/QtEmbeddedLoader.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright (C) 2024 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.app.Activity; | ||
import android.app.Service; | ||
import android.content.ComponentName; | ||
import android.content.Context; | ||
import android.content.ContextWrapper; | ||
import android.content.Intent; | ||
import android.content.pm.PackageManager; | ||
import android.content.pm.PackageManager.NameNotFoundException; | ||
import android.os.Build; | ||
import android.os.Bundle; | ||
import android.util.DisplayMetrics; | ||
import android.util.Log; | ||
import android.view.SurfaceView; | ||
|
||
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.lang.reflect.Field; | ||
import java.lang.reflect.InvocationTargetException; | ||
import java.lang.reflect.Method; | ||
import java.util.ArrayList; | ||
import java.util.Arrays; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
import dalvik.system.DexClassLoader; | ||
import android.content.res.Resources; | ||
|
||
class QtEmbeddedLoader extends QtLoader { | ||
private static final String TAG = "QtEmbeddedLoader"; | ||
|
||
public QtEmbeddedLoader(Context context) { | ||
super(new ContextWrapper(context)); | ||
// TODO Service context handling QTBUG-118874 | ||
} | ||
|
||
@Override | ||
protected void finish() { | ||
// Called when loading fails - clear the delegate to make sure we don't hold reference | ||
// to the embedding Context | ||
QtEmbeddedDelegateFactory.remove((Activity)m_context.getBaseContext()); | ||
} | ||
} |
130 changes: 130 additions & 0 deletions
130
src/android/jar/src/org/qtproject/qt/android/QtView.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// Copyright (C) 2024 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.app.Activity; | ||
import android.content.Context; | ||
import android.content.ContextWrapper; | ||
import android.content.pm.PackageManager.NameNotFoundException; | ||
import android.content.res.Resources; | ||
import android.os.Handler; | ||
import android.os.Looper; | ||
import android.view.View; | ||
import android.view.ViewGroup; | ||
|
||
import java.security.InvalidParameterException; | ||
import java.util.ArrayList; | ||
|
||
// TODO this should not need to extend QtLayout, a simple FrameLayout/ViewGroup should do | ||
// QTBUG-121516 | ||
// Base class for embedding QWindow into native Android view hierarchy. Extend to implement | ||
// the creation of appropriate window to embed. | ||
abstract class QtView extends QtLayout { | ||
private final static String TAG = "QtView"; | ||
|
||
public interface QtWindowListener { | ||
// Called when the QWindow has been created and it's Java counterpart embedded into | ||
// QtView | ||
void onQtWindowLoaded(); | ||
} | ||
|
||
protected QtWindow m_window; | ||
protected long m_windowReference; | ||
protected QtWindowListener m_windowListener; | ||
protected QtEmbeddedDelegate m_delegate; | ||
// Implement in subclass to handle the creation of the QWindow and its parent container. | ||
// TODO could we take care of the parent window creation and parenting outside of the | ||
// window creation method to simplify things if user would extend this? Preferably without | ||
// too much JNI back and forth. Related to parent window creation, so handle with QTBUG-121511. | ||
abstract protected void createWindow(long parentWindowRef); | ||
|
||
private static native void setWindowVisible(long windowReference, boolean visible); | ||
|
||
/** | ||
* Create QtView for embedding a QWindow. Instantiating a QtView will load the Qt libraries | ||
* if they have not already been loaded, including the app library specified by appName, and | ||
* starting the said Qt app. | ||
* @param context the hosting Context | ||
* @param appLibName the name of the Qt app library to load and start. This corresponds to the | ||
target name set in Qt app's CMakeLists.txt | ||
**/ | ||
public QtView(Context context, String appLibName) throws InvalidParameterException { | ||
super(context); | ||
if (appLibName == null || appLibName.isEmpty()) { | ||
throw new InvalidParameterException("QtView: argument 'appLibName' may not be empty "+ | ||
"or null"); | ||
} | ||
|
||
QtEmbeddedLoader loader = new QtEmbeddedLoader(context); | ||
m_delegate = QtEmbeddedDelegateFactory.create((Activity)context); | ||
loader.setMainLibraryName(appLibName); | ||
loader.loadQtLibraries(); | ||
// Start Native Qt application | ||
m_delegate.startNativeApplication(loader.getApplicationParameters(), | ||
loader.getMainLibraryPath()); | ||
} | ||
|
||
@Override | ||
protected void onAttachedToWindow() { | ||
super.onAttachedToWindow(); | ||
m_delegate.setView(this); | ||
m_delegate.queueLoadWindow(); | ||
} | ||
|
||
@Override | ||
protected void onDetachedFromWindow() { | ||
super.onDetachedFromWindow(); | ||
destroyWindow(); | ||
m_delegate.setView(null); | ||
} | ||
|
||
public void setQtWindowListener(QtWindowListener listener) { | ||
m_windowListener = listener; | ||
} | ||
|
||
void setWindowReference(long windowReference) { | ||
m_windowReference = windowReference; | ||
} | ||
|
||
long windowReference() { | ||
return m_windowReference; | ||
} | ||
|
||
// Set the visibility of the underlying QWindow. If visible is true, showNormal() is called. | ||
// If false, the window is hidden. | ||
void setWindowVisible(boolean visible) { | ||
if (m_windowReference != 0L) | ||
setWindowVisible(m_windowReference, true); | ||
} | ||
|
||
// Called from Qt when the QWindow has been created. | ||
// window - the Java QtWindow of the created QAndroidPlatformWindow, to embed into the QtView | ||
// viewReference - the reference to the created QQuickView | ||
void addQtWindow(QtWindow window, long viewReference, long parentWindowRef) { | ||
setWindowReference(viewReference); | ||
m_delegate.setRootWindowRef(parentWindowRef); | ||
final Handler handler = new Handler(Looper.getMainLooper()); | ||
handler.post(new Runnable() { | ||
@Override | ||
public void run() { | ||
m_window = window; | ||
m_window.getLayout().setLayoutParams(new QtLayout.LayoutParams( | ||
ViewGroup.LayoutParams.MATCH_PARENT, | ||
ViewGroup.LayoutParams.MATCH_PARENT)); | ||
addView(m_window.getLayout(), 0); | ||
// Call show window + parent | ||
setWindowVisible(true); | ||
if (m_windowListener != null) | ||
m_windowListener.onQtWindowLoaded(); | ||
} | ||
}); | ||
} | ||
|
||
// Destroy the underlying QWindow | ||
void destroyWindow() { | ||
if (m_windowReference != 0L) | ||
QtEmbeddedDelegate.deleteWindow(m_windowReference); | ||
m_windowReference = 0L; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.