Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qandroidquickviewembedding.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtQuick/private/qandroidquickviewembedding_p.h>
5
6#include <QtCore/qcoreapplication.h>
7#include <QtCore/qjnienvironment.h>
8#include <QtCore/qjniobject.h>
9#include <QtCore/qjniarray.h>
10#include <QtCore/qjnitypes.h>
11#include <QtQml/qqmlengine.h>
12#include <QtQuick/qquickitem.h>
13
15
16Q_DECLARE_JNI_CLASS(QtDelegate, "org/qtproject/qt/android/QtEmbeddedContextDelegate");
17Q_DECLARE_JNI_CLASS(QtQuickView, "org/qtproject/qt/android/QtQuickView");
18Q_DECLARE_JNI_CLASS(QtWindow, "org/qtproject/qt/android/QtWindow");
19Q_DECLARE_JNI_CLASS(View, "android/view/View");
20
21Q_DECLARE_JNI_CLASS(Void, "java/lang/Void");
22Q_DECLARE_JNI_CLASS(Integer, "java/lang/Integer");
23Q_DECLARE_JNI_CLASS(Double, "java/lang/Double");
24Q_DECLARE_JNI_CLASS(Float, "java/lang/Float");
25Q_DECLARE_JNI_CLASS(Boolean, "java/lang/Boolean");
26Q_DECLARE_JNI_CLASS(String, "java/lang/String");
27Q_DECLARE_JNI_CLASS(Class, "java/lang/Class");
28
30{
31 void createQuickView(JNIEnv*, jobject nativeWindow, jstring qmlUri, jint width, jint height,
32 jlong parentWindowReference, QtJniTypes::StringArray qmlImportPaths)
33 {
34 static_assert (sizeof(jlong) >= sizeof(void*),
35 "Insufficient size of Java type to hold the c++ pointer");
36 const QUrl qmlUrl(QJniObject(qmlUri).toString());
37
38 QStringList importPaths;
39 if (qmlImportPaths.isValid()) {
40 QJniArray<QtJniTypes::String> importPathsArray(qmlImportPaths);
41 importPaths.reserve(importPathsArray.size());
42 for (int i = 0; i < importPathsArray.size(); ++i)
43 importPaths << importPathsArray.at(i).toString();
44 }
45
47 parentWindowReference,
48 width,
49 height,
50 qmlUrl,
51 importPaths] {
52 QWindow *parentWindow = reinterpret_cast<QWindow *>(parentWindowReference);
53 QQuickView *view = new QQuickView(parentWindow);
55 new SignalHelper(view);
57 [qtViewObject](QQuickView::Status status) {
58 qtViewObject.callMethod<void>("handleStatusChange", status);
59 });
61 view->setColor(QColor(Qt::transparent));
64 for (const QString &path : importPaths)
65 engine->addImportPath(path);
66
67 const QtJniTypes::QtWindow window = reinterpret_cast<jobject>(view->winId());
68 qtViewObject.callMethod<void>("addQtWindow",
69 window,
70 reinterpret_cast<jlong>(view),
71 parentWindowReference);
72 view->setSource(qmlUrl);
73 });
74 }
75
76 void setRootObjectProperty(JNIEnv *env, jobject object, jlong windowReference,
77 jstring propertyName, jobject value)
78 {
79 Q_UNUSED(env);
80 Q_UNUSED(object);
81
82 QQuickItem *rootObject = reinterpret_cast<QQuickView *>(windowReference)->rootObject();
83 if (!rootObject) {
84 qWarning() << "QtQuickView instance does not own a root object.";
85 return;
86 }
87
88 const QString property = QJniObject(propertyName).toString();
89 const QMetaObject *rootMetaObject = rootObject->metaObject();
90 int propertyIndex = rootMetaObject->indexOfProperty(qPrintable(property));
91 if (propertyIndex < 0) {
92 qWarning("Property %s does not exist in the root QML object.", qPrintable(property));
93 return;
94 }
95
96 QMetaProperty metaProperty = rootMetaObject->property(propertyIndex);
97 const QJniObject propertyValue(value);
98 const QByteArray valueClassname = propertyValue.className();
99
100 if (valueClassname == QtJniTypes::Traits<QtJniTypes::String>::className())
101 metaProperty.write(rootObject, propertyValue.toString());
102 else if (valueClassname == QtJniTypes::Traits<QtJniTypes::Integer>::className())
103 metaProperty.write(rootObject, propertyValue.callMethod<jint>("intValue"));
104 else if (valueClassname == QtJniTypes::Traits<QtJniTypes::Double>::className())
105 metaProperty.write(rootObject, propertyValue.callMethod<jdouble>("doubleValue"));
106 else if (valueClassname == QtJniTypes::Traits<QtJniTypes::Float>::className())
107 metaProperty.write(rootObject, propertyValue.callMethod<jfloat>("floatValue"));
108 else if (valueClassname == QtJniTypes::Traits<QtJniTypes::Boolean>::className())
109 metaProperty.write(rootObject, propertyValue.callMethod<jboolean>("booleanValue"));
110 else
111 qWarning("Setting the property type of %s is not supported.", valueClassname.data());
112 }
113
114 jobject getRootObjectProperty(JNIEnv *env, jobject object, jlong windowReference,
115 jstring propertyName)
116 {
117 Q_UNUSED(object);
118 Q_ASSERT(env);
119
120 const QString property = QJniObject(propertyName).toString();
121 QQuickView *view = reinterpret_cast<QQuickView *>(windowReference);
122 QQuickItem *rootObject = view->rootObject();
123 if (!rootObject) {
124 qWarning("Cannot read property %s as the QtQuickView instance (%s)"
125 "does not own a root object.",
128 return nullptr;
129 }
130
131 const QMetaObject *rootMetaObject = rootObject->metaObject();
132 int propertyIndex = rootMetaObject->indexOfProperty(property.toUtf8().constData());
133 if (propertyIndex < 0) {
134 qWarning("Cannot read property %s as it does not exist in the root QML object.",
136 return nullptr;
137 }
138
139 QMetaProperty metaProperty = rootMetaObject->property(propertyIndex);
140 QVariant propertyValue = metaProperty.read(rootObject);
141 const int propertyTypeId = propertyValue.typeId();
142
143 switch (propertyTypeId) {
144 case QMetaType::Type::Int:
145 return env->NewLocalRef(
146 QJniObject::construct<QtJniTypes::Integer>(get<int>(std::move(propertyValue)))
147 .object());
148 case QMetaType::Type::Double:
149 return env->NewLocalRef(
150 QJniObject::construct<QtJniTypes::Double>(get<double>(std::move(propertyValue)))
151 .object());
152 case QMetaType::Type::Float:
153 return env->NewLocalRef(
154 QJniObject::construct<QtJniTypes::Float>(get<float>(std::move(propertyValue)))
155 .object());
156 case QMetaType::Type::Bool:
157 return env->NewLocalRef(
158 QJniObject::construct<QtJniTypes::Boolean>(get<bool>(std::move(propertyValue)))
159 .object());
160 case QMetaType::Type::QString:
161 return env->NewLocalRef(
162 QJniObject::fromString(get<QString>(std::move(propertyValue))).object());
163 default:
164 qWarning("Property %s cannot be converted to a supported Java data type.",
166 }
167
168 return nullptr;
169 }
170
171 int addRootObjectSignalListener(JNIEnv *env, jobject, jlong windowReference, jstring signalName,
172 jclass argType, jobject listener)
173 {
174 Q_ASSERT(env);
175 static QHash<QByteArray, int> javaToQMetaType = {
176 { "java/lang/Void", QMetaType::Type::Void },
177 { "java/lang/String", QMetaType::Type::QString },
178 { "java/lang/Integer", QMetaType::Type::Int },
179 { "java/lang/Double", QMetaType::Type::Double },
180 { "java/lang/Float", QMetaType::Type::Float },
181 { "java/lang/Boolean", QMetaType::Type::Bool }
182 };
183
184 QQuickView *view = reinterpret_cast<QQuickView *>(windowReference);
185 if (!view) {
186 qWarning() << "QtQuickView is not loaded or ready yet.";
187 return -1;
188 }
189 QQuickItem *rootObject = view->rootObject();
190 if (!rootObject) {
191 qWarning() << "QtQuickView instance does not own a root object.";
192 return -1;
193 }
194
195 SignalHelper *signalHelper = view->findChild<SignalHelper *>();
196 const QByteArray javaArgClass = QJniObject(argType).className();
197 const char *qArgName =
198 QMetaType(javaToQMetaType.value(javaArgClass, QMetaType::Type::UnknownType)).name();
199 const QString signalMethodName = QJniObject(signalName).toString();
200
201 const QMetaObject *metaObject = rootObject->metaObject();
202 int signalIndex = -1;
203 int propertyIndex = -1;
204
206 QStringLiteral("%1(%2)").arg(signalMethodName).arg(QLatin1StringView(qArgName))));
207 signalIndex = metaObject->indexOfSignal(signalSignature.constData());
208
209 // Try to check if the signal is a parameterless notifier of a property
210 // or a property name itself.
211 if (signalIndex == -1) {
212 signalSignature = QMetaObject::normalizedSignature(
213 qPrintable(QStringLiteral("%1()").arg(signalMethodName)));
214 for (int i = 0; i < metaObject->propertyCount(); ++i) {
215 QMetaProperty metaProperty = metaObject->property(i);
216 QMetaMethod notifyMethod = metaProperty.notifySignal();
217
218 if (signalSignature == notifyMethod.methodSignature()) {
219 signalIndex = metaObject->property(i).notifySignalIndex();
220 propertyIndex = i;
221 break;
222 } else if (signalMethodName == QLatin1StringView(metaProperty.name())) {
223 signalIndex = metaObject->property(i).notifySignalIndex();
224 signalSignature = notifyMethod.methodSignature();
225 propertyIndex = i;
226 break;
227 }
228 }
229 }
230
231 if (signalIndex == -1)
232 return -1;
233
234 const QMetaObject *helperMetaObject = signalHelper->metaObject();
235 QByteArray helperSignalSignature = signalSignature;
236 helperSignalSignature.replace(0, signalSignature.indexOf('('), "forwardSignal");
237 int helperSlotIndex = helperMetaObject->indexOfSlot(helperSignalSignature.constData());
238 if (helperSlotIndex == -1)
239 return -1;
240
241 // Return the id if the signal is already connected to the same listener.
242 QJniObject listenerJniObject(listener);
243 if (signalHelper->listenersMap.contains(signalSignature)) {
244 auto listenerInfos = signalHelper->listenersMap.values(signalSignature);
245 auto isSameListener = [listenerJniObject](const SignalHelper::ListenerInfo &listenerInfo) {
246 return listenerInfo.listener == listenerJniObject;
247 };
248 auto iterator = std::find_if(listenerInfos.constBegin(),
249 listenerInfos.constEnd(),
250 isSameListener);
251 if (iterator != listenerInfos.end()) {
252 qWarning("Signal listener with the ID of %i is already connected to %s signal.",
253 iterator->id,
254 signalSignature.constData());
255 return iterator->id;
256 }
257 }
258
259 QMetaMethod signalMethod = metaObject->method(signalIndex);
260 QMetaMethod signalForwarderMethod = helperMetaObject->method(helperSlotIndex);
261 signalHelper->connectionHandleCounter++;
262
264 if (signalHelper->listenersMap.contains(signalSignature)) {
265 connection = signalHelper
266 ->connections[signalHelper->listenersMap.value(signalSignature).id];
267 } else {
268 connection = QObject::connect(rootObject,
269 signalMethod,
270 signalHelper,
271 signalForwarderMethod);
272 }
273
274 SignalHelper::ListenerInfo listenerInfo;
275 listenerInfo.listener = listenerJniObject;
276 listenerInfo.javaArgType = javaArgClass;
277 listenerInfo.propertyIndex = propertyIndex;
278 listenerInfo.signalSignature = signalSignature;
279 listenerInfo.id = signalHelper->connectionHandleCounter;
280
281 signalHelper->listenersMap.insert(signalSignature, listenerInfo);
282 signalHelper->connections.insert(listenerInfo.id, connection);
283
284 return listenerInfo.id;
285 }
286
287 bool removeRootObjectSignalListener(JNIEnv *, jobject, jlong windowReference,
288 jint signalListenerId)
289 {
290 QQuickView *view = reinterpret_cast<QQuickView *>(windowReference);
291 QQuickItem *rootObject = view->rootObject();
292 if (!rootObject) {
293 qWarning() << "QtQuickView instance does not own a root object.";
294 return false;
295 }
296
297 SignalHelper *signalHelper = view->findChild<SignalHelper *>();
298 if (!signalHelper->connections.contains(signalListenerId))
299 return false;
300
301 QByteArray signalSignature;
302 for (auto listenerInfoIter = signalHelper->listenersMap.begin();
303 listenerInfoIter != signalHelper->listenersMap.end();) {
304 if (listenerInfoIter->id == signalListenerId) {
305 signalSignature = listenerInfoIter->signalSignature;
306 signalHelper->listenersMap.erase(listenerInfoIter);
307 break;
308 } else {
309 ++listenerInfoIter;
310 }
311 }
312
313 // disconnect if its the last listener associated with the signal signatures
314 if (!signalHelper->listenersMap.contains(signalSignature))
315 rootObject->disconnect(signalHelper->connections.value(signalListenerId));
316
317 signalHelper->connections.remove(signalListenerId);
318 return true;
319 }
320
325
326 void SignalHelper::forwardSignal(int signalValue)
327 {
329 }
330
331 void SignalHelper::forwardSignal(bool signalValue)
332 {
334 }
335
336 void SignalHelper::forwardSignal(double signalValue)
337 {
339 }
340
341 void SignalHelper::forwardSignal(float signalValue)
342 {
344 }
345
347 {
349 }
350
351 void SignalHelper::invokeListener(QObject *sender, int senderSignalIndex, QVariant signalValue)
352 {
353 using namespace QtJniTypes;
354
355 const QMetaObject *metaObject = sender->metaObject();
356 const QMetaMethod signalMethod = metaObject->method(senderSignalIndex);
357
358 for (auto listenerInfoIter = listenersMap.constFind(signalMethod.methodSignature());
359 listenerInfoIter != listenersMap.constEnd() &&
360 listenerInfoIter.key() == signalMethod.methodSignature();
361 ++listenerInfoIter) {
362 const ListenerInfo listenerInfo = *listenerInfoIter;
363 const QByteArray javaArgType = listenerInfo.javaArgType;
364 QJniObject jSignalMethodName =
365 QJniObject::fromString(QLatin1StringView(signalMethod.name()));
366
367 if (listenerInfo.propertyIndex != -1 && javaArgType != Traits<Void>::className())
368 signalValue = metaObject->property(listenerInfo.propertyIndex).read(sender);
369
370 int valueTypeId = signalValue.typeId();
371 QJniObject jValue;
372
373 switch (valueTypeId) {
374 case QMetaType::Type::UnknownType:
375 break;
376 case QMetaType::Type::Int:
377 jValue = qVariantToJniObject<Integer,jint>(signalValue);
378 break;
379 case QMetaType::Type::Double:
380 jValue = qVariantToJniObject<Double,jdouble>(signalValue);
381 break;
382 case QMetaType::Type::Float:
383 jValue = qVariantToJniObject<Float,jfloat>(signalValue);
384 break;
385 case QMetaType::Type::Bool:
386 jValue = qVariantToJniObject<Boolean,jboolean>(signalValue);
387 break;
388 case QMetaType::Type::QString:
389 jValue = QJniObject::fromString(get<QString>(std::move(signalValue)));
390 break;
391 default:
392 qWarning("Mismatching argument types between QML signal (%s) and the Java function "
393 "(%s). Sending null as argument.",
394 signalMethod.methodSignature().constData(), javaArgType.constData());
395 }
396
397 QNativeInterface::QAndroidApplication::runOnAndroidMainThread(
398 [listenerInfo, jSignalMethodName, jValue]() {
399 listenerInfo.listener.callMethod<void, jstring, jobject>("onSignalEmitted",
400 jSignalMethodName.object<jstring>(),
401 jValue.object());
402 });
403 }
404 }
405
407 return env.registerNativeMethods(QtJniTypes::Traits<QtJniTypes::QtQuickView>::className(),
408 {Q_JNI_NATIVE_SCOPED_METHOD(createQuickView,
410 Q_JNI_NATIVE_SCOPED_METHOD(setRootObjectProperty,
412 Q_JNI_NATIVE_SCOPED_METHOD(getRootObjectProperty,
414 Q_JNI_NATIVE_SCOPED_METHOD(addRootObjectSignalListener,
416 Q_JNI_NATIVE_SCOPED_METHOD(removeRootObjectSignalListener,
418 }
419}
420
421Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
422{
423 Q_UNUSED(vm)
424 Q_UNUSED(reserved)
425
426 static bool initialized = false;
427 if (initialized)
428 return JNI_VERSION_1_6;
429 initialized = true;
430
431 QJniEnvironment env;
432 if (!env.isValid())
433 return JNI_ERR;
435 return JNI_ERR;
436 return JNI_VERSION_1_6;
437}
438
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:124
qsizetype indexOf(char c, qsizetype from=0) const
This is an overloaded member function, provided for convenience. It differs from the above function o...
iterator erase(const_iterator first, const_iterator last)
QByteArray & insert(qsizetype i, QByteArrayView data)
QByteArray & replace(qsizetype index, qsizetype len, const char *s, qsizetype alen)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qbytearray.h:339
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qmetaobject.h:19
\inmodule QtCore Represents a handle to a signal-slot (or signal-functor) connection.
\inmodule QtCore
bool write(QObject *obj, const QVariant &value) const
Writes value as the property's value to the given object.
QVariant read(const QObject *obj) const
Reads the property's value from the given object.
QMetaMethod notifySignal() const
const char * name() const
Returns this property's name.
\inmodule QtCore
Definition qmetatype.h:341
constexpr const char * name() const
Definition qmetatype.h:2680
\inmodule QtCore
Definition qobject.h:103
int senderSignalIndex() const
Definition qobject.cpp:2700
T findChild(QAnyStringView aName, Qt::FindChildOptions options=Qt::FindChildrenRecursively) const
Returns the child of this object that can be cast into type T and that is called name,...
Definition qobject.h:155
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
QObject * sender() const
Returns a pointer to the object that sent the signal, if called in a slot activated by a signal; othe...
Definition qobject.cpp:2658
static bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *member)
\threadsafe
Definition qobject.cpp:3236
The QQmlEngine class provides an environment for instantiating QML components.
Definition qqmlengine.h:57
The QQuickItem class provides the most basic of all visual items in \l {Qt Quick}.
Definition qquickitem.h:63
The QQuickView class provides a window for displaying a Qt Quick user interface.
Definition qquickview.h:20
QUrl source
The URL of the source of the QML component.
Definition qquickview.h:24
void setResizeMode(ResizeMode)
QQmlEngine * engine() const
Returns a pointer to the QQmlEngine used for instantiating QML Components.
@ SizeRootObjectToView
Definition qquickview.h:40
Status
Specifies the loading status of the QQuickView.
Definition qquickview.h:45
void setSource(const QUrl &)
Sets the source to the url, loads the QML component and instantiates it.
QQuickItem * rootObject() const
Returns the view's root \l {QQuickItem} {item}.
void statusChanged(QQuickView::Status)
This signal is emitted when the component's current status changes.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\inmodule QtCore
Definition qurl.h:94
QString toString(FormattingOptions options=FormattingOptions(PrettyDecoded)) const
Returns a string representation of the URL.
Definition qurl.cpp:2831
\inmodule QtCore
Definition qvariant.h:65
int typeId() const
Returns the storage type of the value stored in the variant.
Definition qvariant.h:340
\inmodule QtGui
Definition qwindow.h:63
void setWidth(int arg)
Definition qwindow.cpp:1687
void setHeight(int arg)
Definition qwindow.cpp:1696
void invokeListener(QObject *sender, int senderSignalIndex, QVariant signalValue)
QMultiMap< QByteArray, ListenerInfo > listenersMap
auto signalIndex
static bool registerNatives()
Combined button and popup list for selecting options.
void setRootObjectProperty(JNIEnv *env, jobject object, jlong windowReference, jstring propertyName, jobject value)
void createQuickView(JNIEnv *, jobject nativeWindow, jstring qmlUri, jint width, jint height, jlong parentWindowReference, QtJniTypes::StringArray qmlImportPaths)
bool removeRootObjectSignalListener(JNIEnv *, jobject, jlong windowReference, jint signalListenerId)
bool registerNatives(QJniEnvironment &env)
jobject getRootObjectProperty(JNIEnv *env, jobject object, jlong windowReference, jstring propertyName)
int addRootObjectSignalListener(JNIEnv *env, jobject, jlong windowReference, jstring signalName, jclass argType, jobject listener)
@ transparent
Definition qnamespace.h:47
QT_BEGIN_NAMESPACE Q_DECLARE_JNI_CLASS(QtDelegate, "org/qtproject/qt/android/QtEmbeddedContextDelegate")
Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved)
#define Q_DECL_EXPORT
#define qApp
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
DBusConnection * connection
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qWarning
Definition qlogging.h:166
GLint GLsizei GLsizei height
GLint GLsizei width
GLsizei const GLchar *const * path
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
SSL_CTX int void * arg
#define qPrintable(string)
Definition qstring.h:1531
#define QStringLiteral(str)
#define Q_UNUSED(x)
static QWindowsDirect2DWindow * nativeWindow(QWindow *window)
const char property[13]
Definition qwizard.cpp:101
obj metaObject() -> className()
aWidget window() -> setWindowTitle("New Window Title")
[2]
QQuickView * view
[0]
char * toString(const MyType &t)
[31]
QJSEngine engine
[0]
\inmodule QtCore
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...
static QByteArray normalizedSignature(const char *method)
Normalizes the signature of the given method.