summaryrefslogtreecommitdiffstats
path: root/src/corelib/platform/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/platform/android')
-rw-r--r--src/corelib/platform/android/qandroidextras.cpp1228
-rw-r--r--src/corelib/platform/android/qandroidextras_p.h246
-rw-r--r--src/corelib/platform/android/qandroidnativeinterface.cpp246
3 files changed, 1676 insertions, 44 deletions
diff --git a/src/corelib/platform/android/qandroidextras.cpp b/src/corelib/platform/android/qandroidextras.cpp
new file mode 100644
index 0000000000..aa0c3fd093
--- /dev/null
+++ b/src/corelib/platform/android/qandroidextras.cpp
@@ -0,0 +1,1228 @@
+// Copyright (C) 2021 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
+
+#include "qandroidextras_p.h"
+
+#include <QtCore/qbuffer.h>
+#include <QtCore/qdatastream.h>
+#include <QtCore/qjnienvironment.h>
+#include <QtCore/qvariant.h>
+#include <QtCore/qmutex.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qset.h>
+
+#if QT_CONFIG(future)
+#include <QtCore/qpromise.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidParcelPrivate
+{
+public:
+ QAndroidParcelPrivate();
+ explicit QAndroidParcelPrivate(const QJniObject& parcel);
+
+ void writeData(const QByteArray &data) const;
+ void writeBinder(const QAndroidBinder &binder) const;
+ void writeFileDescriptor(int fd) const;
+
+ QByteArray readData() const;
+ int readFileDescriptor() const;
+ QAndroidBinder readBinder() const;
+
+private:
+ friend class QAndroidBinder;
+ friend class QAndroidParcel;
+ QJniObject handle;
+};
+
+struct FileDescriptor
+{
+ explicit FileDescriptor(int fd = -1)
+ : handle("java/io/FileDescriptor")
+ {
+ QJniEnvironment().checkAndClearExceptions();
+ handle.setField("descriptor", fd);
+ }
+
+ QJniObject handle;
+};
+
+QAndroidParcelPrivate::QAndroidParcelPrivate()
+ : handle(QJniObject::callStaticObjectMethod("android/os/Parcel","obtain",
+ "()Landroid/os/Parcel;").object())
+{}
+
+QAndroidParcelPrivate::QAndroidParcelPrivate(const QJniObject &parcel)
+ : handle(parcel)
+{}
+
+void QAndroidParcelPrivate::writeData(const QByteArray &data) const
+{
+ if (data.isEmpty())
+ return;
+
+ QJniEnvironment().checkAndClearExceptions();
+ QJniEnvironment env;
+ jbyteArray array = env->NewByteArray(data.size());
+ env->SetByteArrayRegion(array, 0, data.length(),
+ reinterpret_cast<const jbyte*>(data.constData()));
+ handle.callMethod<void>("writeByteArray", "([B)V", array);
+ env->DeleteLocalRef(array);
+}
+
+void QAndroidParcelPrivate::writeBinder(const QAndroidBinder &binder) const
+{
+ QJniEnvironment().checkAndClearExceptions();
+ handle.callMethod<void>("writeStrongBinder", "(Landroid/os/IBinder;)V",
+ binder.handle().object());
+}
+
+void QAndroidParcelPrivate::writeFileDescriptor(int fd) const
+{
+ QJniEnvironment().checkAndClearExceptions();
+ handle.callMethod<void>("writeFileDescriptor", "(Ljava/io/FileDescriptor;)V",
+ FileDescriptor(fd).handle.object());
+}
+
+QByteArray QAndroidParcelPrivate::readData() const
+{
+ QJniEnvironment().checkAndClearExceptions();
+ auto array = handle.callObjectMethod("createByteArray", "()[B");
+ QJniEnvironment env;
+ auto sz = env->GetArrayLength(jbyteArray(array.object()));
+ QByteArray res(sz, Qt::Initialization::Uninitialized);
+ env->GetByteArrayRegion(jbyteArray(array.object()), 0, sz,
+ reinterpret_cast<jbyte *>(res.data()));
+ return res;
+}
+
+int QAndroidParcelPrivate::readFileDescriptor() const
+{
+ QJniEnvironment().checkAndClearExceptions();
+ auto parcelFD = handle.callObjectMethod("readFileDescriptor",
+ "()Landroid/os/ParcelFileDescriptor;");
+ if (parcelFD.isValid())
+ return parcelFD.callMethod<jint>("getFd", "()I");
+ return -1;
+}
+
+QAndroidBinder QAndroidParcelPrivate::readBinder() const
+{
+ QJniEnvironment().checkAndClearExceptions();
+ auto strongBinder = handle.callObjectMethod("readStrongBinder", "()Landroid/os/IBinder;");
+ return QAndroidBinder(strongBinder.object());
+}
+
+/*!
+ \class QAndroidParcel
+ \inheaderfile QtCore/private/qandroidextras_p.h
+ \preliminary
+ \inmodule QtCorePrivate
+ \brief Wraps the most important methods of Android Parcel class.
+
+ The QAndroidParcel is a convenience class that wraps the most important
+ \l {https://developer.android.com/reference/android/os/Parcel.html}{Android Parcel}
+ methods.
+
+ \include qtcore.qdoc qtcoreprivate-usage
+
+ \since 6.2
+*/
+
+/*!
+ Creates a new object.
+ */
+QAndroidParcel::QAndroidParcel()
+ : d(new QAndroidParcelPrivate())
+{
+}
+
+/*!
+ Wraps the \a parcel object.
+ */
+QAndroidParcel::QAndroidParcel(const QJniObject& parcel)
+ : d(new QAndroidParcelPrivate(parcel))
+{
+
+}
+
+QAndroidParcel::~QAndroidParcel()
+{
+}
+
+/*!
+ Writes the provided \a data as a byte array
+ */
+void QAndroidParcel::writeData(const QByteArray &data) const
+{
+ d->writeData(data);
+}
+
+/*!
+ Writes the provided \a value. The value is converted into a
+ QByteArray before is written.
+ */
+void QAndroidParcel::writeVariant(const QVariant &value) const
+{
+ QByteArray buff;
+ QDataStream stream(&buff, QIODevice::WriteOnly);
+ stream << value;
+ d->writeData(buff);
+}
+
+/*!
+ Writes a \a binder object. This is useful for a client to
+ send to a server a binder which can be used by the server callback the client.
+ */
+void QAndroidParcel::writeBinder(const QAndroidBinder &binder) const
+{
+ d->writeBinder(binder);
+}
+
+/*!
+ Writes the provided \a fd.
+ */
+void QAndroidParcel::writeFileDescriptor(int fd) const
+{
+ d->writeFileDescriptor(fd);
+}
+
+/*!
+ Returns the data as a QByteArray
+ */
+QByteArray QAndroidParcel::readData() const
+{
+ return d->readData();
+}
+
+/*!
+ Returns the data as a QVariant
+ */
+QVariant QAndroidParcel::readVariant() const
+{
+ QDataStream stream(d->readData());
+ QVariant res;
+ stream >> res;
+ return res;
+}
+
+/*!
+ Returns the binder as a QAndroidBinder
+ */
+QAndroidBinder QAndroidParcel::readBinder() const
+{
+ return d->readBinder();
+}
+
+/*!
+ Returns the file descriptor
+ */
+int QAndroidParcel::readFileDescriptor() const
+{
+ return d->readFileDescriptor();
+}
+
+/*!
+ The return value is useful to call other Java API which are not covered by this wrapper
+ */
+QJniObject QAndroidParcel::handle() const
+{
+ return d->handle;
+}
+
+
+
+/*!
+ \class QAndroidBinder
+ \inheaderfile QtCore/private/qandroidextras_p.h
+ \preliminary
+ \inmodule QtCorePrivate
+ \brief Wraps the most important methods of Android Binder class.
+
+ The QAndroidBinder is a convenience class that wraps the most important
+ \l {https://developer.android.com/reference/android/os/Binder.html}{Android Binder}
+ methods.
+
+ \include qtcore.qdoc qtcoreprivate-usage
+
+ \since 6.2
+*/
+
+
+/*!
+ \enum QAndroidBinder::CallType
+
+ This enum is used with \l QAndroidBinder::transact() to describe the mode in which the
+ IPC call is performed.
+
+ \value Normal normal IPC, meaning that the caller waits the result from the callee
+ \value OneWay one-way IPC, meaning that the caller returns immediately, without waiting
+ for a result from the callee
+*/
+
+
+class QAndroidBinderPrivate
+{
+public:
+ explicit QAndroidBinderPrivate(QAndroidBinder *binder)
+ : handle("org/qtproject/qt/android/extras/QtAndroidBinder", "(J)V", jlong(binder))
+ , m_isQtAndroidBinder(true)
+ {
+ QJniEnvironment().checkAndClearExceptions();
+ }
+
+ explicit QAndroidBinderPrivate(const QJniObject &binder)
+ : handle(binder), m_isQtAndroidBinder(false) {};
+ void setDeleteListener(const std::function<void()> &func) { m_deleteListener = func; }
+ ~QAndroidBinderPrivate()
+ {
+ if (m_isQtAndroidBinder) {
+ QJniEnvironment().checkAndClearExceptions();
+ handle.callMethod<void>("setId", "(J)V", jlong(0));
+ if (m_deleteListener)
+ m_deleteListener();
+ }
+ }
+
+private:
+ QJniObject handle;
+ std::function<void()> m_deleteListener;
+ bool m_isQtAndroidBinder;
+ friend class QAndroidBinder;
+};
+
+/*!
+ Creates a new object which can be used to perform IPC.
+
+ \sa onTransact, transact
+ */
+QAndroidBinder::QAndroidBinder()
+ : d(new QAndroidBinderPrivate(this))
+{
+}
+
+/*!
+ Creates a new object from the \a binder Java object.
+
+ \sa transact
+ */
+QAndroidBinder::QAndroidBinder(const QJniObject &binder)
+ : d(new QAndroidBinderPrivate(binder))
+{
+}
+
+QAndroidBinder::~QAndroidBinder()
+{
+}
+
+/*!
+ Default implementation is a stub that returns false.
+ The user should override this method to get the transact data from the caller.
+
+ The \a code is the action to perform.
+ The \a data is the marshaled data sent by the caller.\br
+ The \a reply is the marshaled data to be sent to the caller.\br
+ The \a flags are the additional operation flags.\br
+
+ \warning This method is called from Binder's thread which is different
+ from the thread that this object was created.
+
+ \sa transact
+ */
+bool QAndroidBinder::onTransact(int /*code*/, const QAndroidParcel &/*data*/,
+ const QAndroidParcel &/*reply*/, CallType /*flags*/)
+{
+ return false;
+}
+
+/*!
+ Performs an IPC call
+
+ The \a code is the action to perform. Should be between
+ \l {https://developer.android.com/reference/android/os/IBinder.html#FIRST_CALL_TRANSACTION}
+ {FIRST_CALL_TRANSACTION} and
+ \l {https://developer.android.com/reference/android/os/IBinder.html#LAST_CALL_TRANSACTION}
+ {LAST_CALL_TRANSACTION}.\br
+ The \a data is the marshaled data to send to the target.\br
+ The \a reply (if specified) is the marshaled data to be received from the target.
+ May be \b nullptr if you are not interested in the return value.\br
+ The \a flags are the additional operation flags.\br
+
+ \return true on success
+ */
+bool QAndroidBinder::transact(int code, const QAndroidParcel &data,
+ QAndroidParcel *reply, CallType flags) const
+{
+ QJniEnvironment().checkAndClearExceptions();
+ return d->handle.callMethod<jboolean>("transact",
+ "(ILandroid/os/Parcel;Landroid/os/Parcel;I)Z",
+ jint(code), data.d->handle.object(),
+ reply ? reply->d->handle.object() : nullptr,
+ jint(flags));
+}
+
+/*!
+ The return value is useful to call other Java API which are not covered by this wrapper
+ */
+QJniObject QAndroidBinder::handle() const
+{
+ return d->handle;
+}
+
+
+
+
+/*!
+ \class QAndroidServiceConnection
+ \inheaderfile QtCore/private/qandroidextras_p.h
+ \preliminary
+ \inmodule QtCorePrivate
+ \brief Wraps the most important methods of Android ServiceConnection class.
+
+ The QAndroidServiceConnection is a convenience abstract class which wraps the
+ \l {https://developer.android.com/reference/android/content/ServiceConnection.html}{AndroidServiceConnection}
+ interface.
+
+ It is useful when you perform a QtAndroidPrivate::bindService operation.
+
+ \include qtcore.qdoc qtcoreprivate-usage
+
+ \since 6.2
+*/
+
+/*!
+ Creates a new object
+ */
+QAndroidServiceConnection::QAndroidServiceConnection()
+ : m_handle("org/qtproject/qt/android/extras/QtAndroidServiceConnection", "(J)V", jlong(this))
+{
+}
+
+/*!
+ Creates a new object from an existing \a serviceConnection.
+
+ It's useful when you have your own Java implementation.
+ Of course onServiceConnected()/onServiceDisconnected()
+ will not be called anymore.
+ */
+QAndroidServiceConnection::QAndroidServiceConnection(const QJniObject &serviceConnection)
+ : m_handle(serviceConnection)
+{
+}
+
+QAndroidServiceConnection::~QAndroidServiceConnection()
+{
+ m_handle.callMethod<void>("setId", "(J)V", jlong(this));
+}
+
+/*!
+ returns the underline QJniObject
+ */
+QJniObject QAndroidServiceConnection::handle() const
+{
+ return m_handle;
+}
+
+/*!
+ \fn void QAndroidServiceConnection::onServiceConnected(const QString &name, const QAndroidBinder &serviceBinder)
+
+ This notification is called when the client managed to connect to the service.
+ The \a name contains the server name, the \a serviceBinder is the binder that the client
+ uses to perform IPC operations.
+
+ \warning This method is called from Binder's thread which is different
+ from the thread that this object was created.
+
+ returns the underline QJniObject
+ */
+
+/*!
+ \fn void QAndroidServiceConnection::onServiceDisconnected(const QString &name)
+
+ Called when a connection to the Service has been lost.
+ The \a name parameter specifies which connectioen was lost.
+
+ \warning This method is called from Binder's thread which is different
+ from the thread that this object was created.
+
+ returns the underline QJniObject
+ */
+
+
+Q_CONSTINIT static QBasicAtomicInteger<uint> nextUniqueActivityRequestCode = Q_BASIC_ATOMIC_INITIALIZER(0);
+
+// Get a unique activity request code.
+static int uniqueActivityRequestCode()
+{
+ constexpr uint ReservedForQtOffset = 0x1000; // Reserve all request codes under 0x1000 for Qt
+
+ const uint requestCodeBase = nextUniqueActivityRequestCode.fetchAndAddRelaxed(1);
+ if (requestCodeBase == uint(INT_MAX) - ReservedForQtOffset)
+ qWarning("Unique activity request code has wrapped. Unexpected behavior may occur.");
+
+ const int requestCode = static_cast<int>(requestCodeBase + ReservedForQtOffset);
+ return requestCode;
+}
+
+class QAndroidActivityResultReceiverPrivate: public QtAndroidPrivate::ActivityResultListener
+{
+public:
+ QAndroidActivityResultReceiver *q;
+ mutable QHash<int, int> localToGlobalRequestCode;
+ mutable QHash<int, int> globalToLocalRequestCode;
+
+ int globalRequestCode(int localRequestCode) const
+ {
+ const auto oldSize = localToGlobalRequestCode.size();
+ auto &e = localToGlobalRequestCode[localRequestCode];
+ if (localToGlobalRequestCode.size() != oldSize) {
+ // new entry, populate:
+ int globalRequestCode = uniqueActivityRequestCode();
+ e = globalRequestCode;
+ globalToLocalRequestCode[globalRequestCode] = localRequestCode;
+ }
+ return e;
+ }
+
+ bool handleActivityResult(jint requestCode, jint resultCode, jobject data)
+ {
+ const auto it = std::as_const(globalToLocalRequestCode).find(requestCode);
+ if (it != globalToLocalRequestCode.cend()) {
+ q->handleActivityResult(*it, resultCode, QJniObject(data));
+ return true;
+ }
+
+ return false;
+ }
+
+ static QAndroidActivityResultReceiverPrivate *get(QAndroidActivityResultReceiver *publicObject)
+ {
+ return publicObject->d.data();
+ }
+};
+
+/*!
+ \class QAndroidActivityResultReceiver
+ \inheaderfile QtCore/private/qandroidextras_p.h
+ \preliminary
+ \inmodule QtCorePrivate
+ \since 6.2
+ \brief Interface used for callbacks from onActivityResult() in the main Android activity.
+
+ Create a subclass of this class to be notified of the results when using the
+ \c QtAndroidPrivate::startActivity() and \c QtAndroidPrivate::startIntentSender() APIs.
+
+ \include qtcore.qdoc qtcoreprivate-usage
+ */
+
+/*!
+ \internal
+*/
+QAndroidActivityResultReceiver::QAndroidActivityResultReceiver()
+ : d(new QAndroidActivityResultReceiverPrivate)
+{
+ d->q = this;
+ QtAndroidPrivate::registerActivityResultListener(d.data());
+}
+
+/*!
+ \internal
+*/
+QAndroidActivityResultReceiver::~QAndroidActivityResultReceiver()
+{
+ QtAndroidPrivate::unregisterActivityResultListener(d.data());
+}
+
+/*!
+ \fn void QAndroidActivityResultReceiver::handleActivityResult(int receiverRequestCode, int resultCode, const QJniObject &data)
+
+ Reimplement this function to get activity results after starting an activity using
+ either QtAndroidPrivate::startActivity() or QtAndroidPrivate::startIntentSender().
+ The \a receiverRequestCode is the request code unique to this receiver which was
+ originally passed to the startActivity() or startIntentSender() functions. The
+ \a resultCode is the result returned by the activity, and \a data is either null
+ or a Java object of the class android.content.Intent. Both the last to arguments
+ are identical to the arguments passed to onActivityResult().
+*/
+
+
+
+class QAndroidServicePrivate : public QObject, public QtAndroidPrivate::OnBindListener
+{
+public:
+ QAndroidServicePrivate(QAndroidService *service,
+ const std::function<QAndroidBinder*(const QAndroidIntent&)> &binder ={})
+ : m_service(service)
+ , m_binder(binder)
+ {
+ QTimer::singleShot(0,this, [this]{ QtAndroidPrivate::setOnBindListener(this);});
+ }
+
+ ~QAndroidServicePrivate()
+ {
+ QMutexLocker lock(&m_bindersMutex);
+ while (!m_binders.empty()) {
+ auto it = m_binders.begin();
+ lock.unlock();
+ delete (*it);
+ lock.relock();
+ }
+ }
+
+ // OnBindListener interface
+ jobject onBind(jobject intent) override
+ {
+ auto qai = QAndroidIntent(intent);
+ auto binder = m_binder ? m_binder(qai) : m_service->onBind(qai);
+ if (binder) {
+ {
+ QMutexLocker lock(&m_bindersMutex);
+ binder->d->setDeleteListener([this, binder]{binderDestroied(binder);});
+ m_binders.insert(binder);
+ }
+ return binder->handle().object();
+ }
+ return nullptr;
+ }
+
+private:
+ void binderDestroied(QAndroidBinder* obj)
+ {
+ QMutexLocker lock(&m_bindersMutex);
+ m_binders.remove(obj);
+ }
+
+public:
+ QAndroidService *m_service = nullptr;
+ std::function<QAndroidBinder *(const QAndroidIntent &)> m_binder;
+ QMutex m_bindersMutex;
+ QSet<QAndroidBinder*> m_binders;
+};
+
+/*!
+ \class QAndroidService
+ \inheaderfile QtCore/private/qandroidextras_p.h
+ \preliminary
+ \inmodule QtCorePrivate
+ \brief Wraps the most important methods of Android Service class.
+
+ The QAndroidService is a convenience class that wraps the most important
+ \l {https://developer.android.com/reference/android/app/Service.html}{Android Service}
+ methods.
+
+ \include qtcore.qdoc qtcoreprivate-usage
+
+ \since 6.2
+*/
+
+
+/*!
+ \fn QAndroidService::QAndroidService(int &argc, char **argv)
+
+ Creates a new Android service, passing \a argc and \a argv as parameters.
+
+ //! Parameter \a flags is omitted in the documentation.
+
+ \sa QCoreApplication
+ */
+QAndroidService::QAndroidService(int &argc, char **argv, int flags)
+ : QCoreApplication (argc, argv, QtAndroidPrivate::acuqireServiceSetup(flags))
+ , d(new QAndroidServicePrivate{this})
+{
+}
+
+/*!
+ \fn QAndroidService::QAndroidService(int &argc, char **argv, const std::function<QAndroidBinder *(const QAndroidIntent &)> &binder)
+
+ Creates a new Android service, passing \a argc and \a argv as parameters.
+
+ \a binder is used to create a \l {QAndroidBinder}{binder} when needed.
+
+ //! Parameter \a flags is omitted in the documentation.
+
+ \sa QCoreApplication
+ */
+QAndroidService::QAndroidService(int &argc, char **argv,
+ const std::function<QAndroidBinder*(const QAndroidIntent&)> &binder,
+ int flags)
+ : QCoreApplication (argc, argv, QtAndroidPrivate::acuqireServiceSetup(flags))
+ , d(new QAndroidServicePrivate{this, binder})
+{
+}
+
+QAndroidService::~QAndroidService()
+{}
+
+/*!
+ The user must override this method and to return a binder.
+
+ The \a intent parameter contains all the caller information.
+
+ The returned binder is used by the caller to perform IPC calls.
+
+ \warning This method is called from Binder's thread which is different
+ from the thread that this object was created.
+
+ \sa QAndroidBinder::onTransact, QAndroidBinder::transact
+ */
+QAndroidBinder* QAndroidService::onBind(const QAndroidIntent &/*intent*/)
+{
+ return nullptr;
+}
+
+static jboolean onTransact(JNIEnv */*env*/, jclass /*cls*/, jlong id, jint code, jobject data,
+ jobject reply, jint flags)
+{
+ if (!id)
+ return false;
+
+ return reinterpret_cast<QAndroidBinder*>(id)->onTransact(
+ code, QAndroidParcel(data), QAndroidParcel(reply), QAndroidBinder::CallType(flags));
+}
+
+static void onServiceConnected(JNIEnv */*env*/, jclass /*cls*/, jlong id, jstring name,
+ jobject service)
+{
+ if (!id)
+ return;
+
+ return reinterpret_cast<QAndroidServiceConnection *>(id)->onServiceConnected(
+ QJniObject(name).toString(), QAndroidBinder(service));
+}
+
+static void onServiceDisconnected(JNIEnv */*env*/, jclass /*cls*/, jlong id, jstring name)
+{
+ if (!id)
+ return;
+
+ return reinterpret_cast<QAndroidServiceConnection *>(id)->onServiceDisconnected(
+ QJniObject(name).toString());
+}
+
+bool QtAndroidPrivate::registerExtrasNatives(QJniEnvironment &env)
+{
+ static const JNINativeMethod methods[] = {
+ {"onTransact", "(JILandroid/os/Parcel;Landroid/os/Parcel;I)Z", (void *)onTransact},
+ {"onServiceConnected", "(JLjava/lang/String;Landroid/os/IBinder;)V", (void *)onServiceConnected},
+ {"onServiceDisconnected", "(JLjava/lang/String;)V", (void *)onServiceDisconnected}
+ };
+
+ return env.registerNativeMethods("org/qtproject/qt/android/extras/QtNative", methods, 3);
+}
+
+/*!
+ \class QAndroidIntent
+ \inheaderfile QtCore/private/qandroidextras_p.h
+ \preliminary
+ \inmodule QtCorePrivate
+ \brief Wraps the most important methods of Android Intent class.
+
+ The QAndroidIntent is a convenience class that wraps the most important
+ \l {https://developer.android.com/reference/android/content/Intent.html}{Android Intent}
+ methods.
+
+ \include qtcore.qdoc qtcoreprivate-usage
+
+ \since 6.2
+*/
+
+/*!
+ Create a new intent
+ */
+QAndroidIntent::QAndroidIntent()
+ : m_handle("android.content.Intent", "()V")
+{
+
+}
+
+QAndroidIntent::~QAndroidIntent()
+{}
+
+/*!
+ Wraps the provided \a intent java object.
+ */
+QAndroidIntent::QAndroidIntent(const QJniObject &intent)
+ : m_handle(intent)
+{
+}
+
+/*!
+ Creates a new intent and sets the provided \a action.
+ */
+QAndroidIntent::QAndroidIntent(const QString &action)
+ : m_handle("android.content.Intent", "(Ljava/lang/String;)V",
+ QJniObject::fromString(action).object())
+{
+ QJniEnvironment().checkAndClearExceptions();
+}
+
+/*!
+ Creates a new intent and sets the provided \a packageContext and the service \a className.
+ Example:
+ \code
+ auto serviceIntent = QAndroidIntent(QtAndroidPrivate::androidActivity().object(), "com.example.MyService");
+ \endcode
+
+ \sa QtAndroidPrivate::bindService
+ */
+QAndroidIntent::QAndroidIntent(const QJniObject &packageContext, const char *className)
+ : m_handle("android/content/Intent", "(Landroid/content/Context;Ljava/lang/Class;)V",
+ packageContext.object(), QJniEnvironment().findClass(className))
+{
+ QJniEnvironment().checkAndClearExceptions();
+}
+
+/*!
+ Sets the \a key with the \a data in the Intent extras
+ */
+void QAndroidIntent::putExtra(const QString &key, const QByteArray &data)
+{
+ QJniEnvironment().checkAndClearExceptions();
+ QJniEnvironment env;
+ jbyteArray array = env->NewByteArray(data.size());
+ env->SetByteArrayRegion(array, 0, data.length(),
+ reinterpret_cast<const jbyte*>(data.constData()));
+ m_handle.callObjectMethod("putExtra", "(Ljava/lang/String;[B)Landroid/content/Intent;",
+ QJniObject::fromString(key).object(), array);
+ env->DeleteLocalRef(array);
+ QJniEnvironment().checkAndClearExceptions();
+}
+
+/*!
+ Returns the extra \a key data from the Intent extras
+ */
+QByteArray QAndroidIntent::extraBytes(const QString &key)
+{
+ QJniEnvironment().checkAndClearExceptions();
+ auto array = m_handle.callObjectMethod("getByteArrayExtra", "(Ljava/lang/String;)[B",
+ QJniObject::fromString(key).object());
+ if (!array.isValid() || !array.object())
+ return QByteArray();
+ QJniEnvironment env;
+ auto sz = env->GetArrayLength(jarray(array.object()));
+ QByteArray res(sz, Qt::Initialization::Uninitialized);
+ env->GetByteArrayRegion(jbyteArray(array.object()), 0, sz,
+ reinterpret_cast<jbyte *>(res.data()));
+ QJniEnvironment().checkAndClearExceptions();
+
+ return res;
+}
+
+/*!
+ Sets the \a key with the \a value in the Intent extras.
+ */
+void QAndroidIntent::putExtra(const QString &key, const QVariant &value)
+{
+ QByteArray buff;
+ QDataStream stream(&buff, QIODevice::WriteOnly);
+ stream << value;
+ putExtra(key, buff);
+}
+
+/*!
+ Returns the extra \a key data from the Intent extras as a QVariant
+ */
+QVariant QAndroidIntent::extraVariant(const QString &key)
+{
+ QDataStream stream(extraBytes(key));
+ QVariant res;
+ stream >> res;
+ return res;
+}
+
+/*!
+ The return value is useful to call other Java API which are not covered by this wrapper
+ */
+QJniObject QAndroidIntent::handle() const
+{
+ return m_handle;
+}
+
+
+
+/*!
+ \namespace QtAndroidPrivate
+ \preliminary
+ \inmodule QtCorePrivate
+ \since 6.2
+ \brief The QtAndroidPrivate namespace provides miscellaneous functions
+ to aid Android development.
+ \inheaderfile QtCore/private/qandroidextras_p.h
+
+ \include qtcore.qdoc qtcoreprivate-usage
+*/
+
+/*!
+ \since 6.2
+ \enum QtAndroidPrivate::BindFlag
+
+ This enum is used with QtAndroidPrivate::bindService to describe the mode in which the
+ binding is performed.
+
+ \value None No options.
+ \value AutoCreate Automatically creates the service as long as the binding exist.
+ See \l {https://developer.android.com/reference/android/content/Context.html#BIND_AUTO_CREATE}
+ {BIND_AUTO_CREATE} documentation for more details.
+ \value DebugUnbind Include debugging help for mismatched calls to unbind.
+ See \l {https://developer.android.com/reference/android/content/Context.html#BIND_DEBUG_UNBIND}
+ {BIND_DEBUG_UNBIND} documentation for more details.
+ \value NotForeground Don't allow this binding to raise the target service's process to the foreground scheduling priority.
+ See \l {https://developer.android.com/reference/android/content/Context.html#BIND_NOT_FOREGROUND}
+ {BIND_NOT_FOREGROUND} documentation for more details.
+ \value AboveClient Indicates that the client application binding to this service considers the service to be more important than the app itself.
+ See \l {https://developer.android.com/reference/android/content/Context.html#BIND_ABOVE_CLIENT}
+ {BIND_ABOVE_CLIENT} documentation for more details.
+ \value AllowOomManagement Allow the process hosting the bound service to go through its normal memory management.
+ See \l {https://developer.android.com/reference/android/content/Context.html#BIND_ALLOW_OOM_MANAGEMENT}
+ {BIND_ALLOW_OOM_MANAGEMENT} documentation for more details.
+ \value WaivePriority Don't impact the scheduling or memory management priority of the target service's hosting process.
+ See \l {https://developer.android.com/reference/android/content/Context.html#BIND_WAIVE_PRIORITY}
+ {BIND_WAIVE_PRIORITY} documentation for more details.
+ \value Important This service is assigned a higher priority so that it is available to the client when needed.
+ See \l {https://developer.android.com/reference/android/content/Context.html#BIND_IMPORTANT}
+ {BIND_IMPORTANT} documentation for more details.
+ \value AdjustWithActivity If binding from an activity, allow the target service's process importance to be raised based on whether the activity is visible to the user.
+ See \l {https://developer.android.com/reference/android/content/Context.html#BIND_ADJUST_WITH_ACTIVITY}
+ {BIND_ADJUST_WITH_ACTIVITY} documentation for more details.
+ \value ExternalService The service being bound is an isolated, external service.
+ See \l {https://developer.android.com/reference/android/content/Context.html#BIND_EXTERNAL_SERVICE}
+ {BIND_EXTERNAL_SERVICE} documentation for more details.
+*/
+
+/*!
+ \since 6.2
+
+ Starts the activity given by \a intent and provides the result asynchronously through the
+ \a resultReceiver if this is non-null.
+
+ If \a resultReceiver is null, then the \c startActivity() method in the \c androidActivity()
+ will be called. Otherwise \c startActivityForResult() will be called.
+
+ The \a receiverRequestCode is a request code unique to the \a resultReceiver, and will be
+ returned along with the result, making it possible to use the same receiver for more than
+ one intent.
+
+ */
+void QtAndroidPrivate::startActivity(const QJniObject &intent,
+ int receiverRequestCode,
+ QAndroidActivityResultReceiver *resultReceiver)
+{
+ QJniObject activity = QtAndroidPrivate::activity();
+ if (resultReceiver != 0) {
+ QAndroidActivityResultReceiverPrivate *resultReceiverD =
+ QAndroidActivityResultReceiverPrivate::get(resultReceiver);
+ activity.callMethod<void>("startActivityForResult",
+ "(Landroid/content/Intent;I)V",
+ intent.object<jobject>(),
+ resultReceiverD->globalRequestCode(receiverRequestCode));
+ } else {
+ activity.callMethod<void>("startActivity",
+ "(Landroid/content/Intent;)V",
+ intent.object<jobject>());
+ }
+}
+
+/*!
+ \since 6.2
+
+ Starts the activity given by \a intent and provides the result asynchronously through the
+ \a resultReceiver if this is non-null.
+
+ If \a resultReceiver is null, then the \c startActivity() method in the \c androidActivity()
+ will be called. Otherwise \c startActivityForResult() will be called.
+
+ The \a receiverRequestCode is a request code unique to the \a resultReceiver, and will be
+ returned along with the result, making it possible to use the same receiver for more than
+ one intent.
+
+ */
+void QtAndroidPrivate::startActivity(const QAndroidIntent &intent,
+ int receiverRequestCode,
+ QAndroidActivityResultReceiver *resultReceiver)
+{
+ startActivity(intent.handle(), receiverRequestCode, resultReceiver);
+}
+
+/*!
+ \since 6.2
+
+ Starts the activity given by \a intent, using the request code \a receiverRequestCode,
+ and provides the result by calling \a callbackFunc.
+*/
+void QtAndroidPrivate::startActivity(const QJniObject &intent,
+ int receiverRequestCode,
+ std::function<void(int, int, const QJniObject &data)> callbackFunc)
+{
+ QJniObject activity = QtAndroidPrivate::activity();
+ QAndroidActivityCallbackResultReceiver::instance()->registerCallback(receiverRequestCode,
+ callbackFunc);
+ startActivity(intent, receiverRequestCode, QAndroidActivityCallbackResultReceiver::instance());
+}
+
+/*!
+ \since 6.2
+
+ Starts the activity given by \a intentSender and provides the result asynchronously through the
+ \a resultReceiver if this is non-null.
+
+ If \a resultReceiver is null, then the \c startIntentSender() method in the \c androidActivity()
+ will be called. Otherwise \c startIntentSenderForResult() will be called.
+
+ The \a receiverRequestCode is a request code unique to the \a resultReceiver, and will be
+ returned along with the result, making it possible to use the same receiver for more than
+ one intent.
+
+*/
+void QtAndroidPrivate::startIntentSender(const QJniObject &intentSender,
+ int receiverRequestCode,
+ QAndroidActivityResultReceiver *resultReceiver)
+{
+ QJniObject activity = QtAndroidPrivate::activity();
+ if (resultReceiver != 0) {
+ QAndroidActivityResultReceiverPrivate *resultReceiverD =
+ QAndroidActivityResultReceiverPrivate::get(resultReceiver);
+ activity.callMethod<void>("startIntentSenderForResult",
+ "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V",
+ intentSender.object<jobject>(),
+ resultReceiverD->globalRequestCode(receiverRequestCode),
+ 0, // fillInIntent
+ 0, // flagsMask
+ 0, // flagsValues
+ 0); // extraFlags
+ } else {
+ activity.callMethod<void>("startIntentSender",
+ "(Landroid/content/IntentSender;Landroid/content/Intent;III)V",
+ intentSender.object<jobject>(),
+ 0, // fillInIntent
+ 0, // flagsMask
+ 0, // flagsValues
+ 0); // extraFlags
+
+ }
+
+}
+
+/*!
+ \since 6.2
+ \fn bool QtAndroidPrivate::bindService(const QAndroidIntent &serviceIntent, const QAndroidServiceConnection &serviceConnection, BindFlags flags = BindFlag::None)
+
+ Binds the service given by \a serviceIntent, \a serviceConnection and \a flags.
+ The \a serviceIntent object identifies the service to connect to.
+ The \a serviceConnection is a listener that receives the information as the service
+ is started and stopped.
+
+ \return true on success
+
+ See \l {https://developer.android.com/reference/android/content/Context.html#bindService%28android.content.Intent,%20android.content.ServiceConnection,%20int%29}
+ {Android documentation} documentation for more details.
+
+ \sa QAndroidIntent, QAndroidServiceConnection, BindFlag
+*/
+bool QtAndroidPrivate::bindService(const QAndroidIntent &serviceIntent,
+ const QAndroidServiceConnection &serviceConnection, BindFlags flags)
+{
+ QJniEnvironment().checkAndClearExceptions();
+ QJniObject contextObj = QtAndroidPrivate::context();
+ return contextObj.callMethod<jboolean>(
+ "bindService",
+ "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z",
+ serviceIntent.handle().object(),
+ serviceConnection.handle().object(),
+ jint(flags));
+}
+
+QAndroidActivityCallbackResultReceiver * QAndroidActivityCallbackResultReceiver::s_instance = nullptr;
+
+QAndroidActivityCallbackResultReceiver::QAndroidActivityCallbackResultReceiver()
+ : QAndroidActivityResultReceiver()
+ , callbackMap()
+{
+}
+
+void QAndroidActivityCallbackResultReceiver::handleActivityResult(int receiverRequestCode,
+ int resultCode,
+ const QJniObject &intent)
+{
+ callbackMap[receiverRequestCode](receiverRequestCode, resultCode, intent);
+ callbackMap.remove(receiverRequestCode);
+}
+
+QAndroidActivityCallbackResultReceiver * QAndroidActivityCallbackResultReceiver::instance() {
+ if (!s_instance) {
+ s_instance = new QAndroidActivityCallbackResultReceiver();
+ }
+ return s_instance;
+}
+
+void QAndroidActivityCallbackResultReceiver::registerCallback(
+ int receiverRequestCode,
+ std::function<void(int, int, const QJniObject &data)> callbackFunc)
+{
+ callbackMap.insert(receiverRequestCode, callbackFunc);
+}
+
+// Permissions API
+
+static const char qtNativeClassName[] = "org/qtproject/qt/android/QtNative";
+
+QtAndroidPrivate::PermissionResult resultFromAndroid(jint value)
+{
+ return value == 0 ? QtAndroidPrivate::Authorized : QtAndroidPrivate::Denied;
+}
+
+using PendingPermissionRequestsHash
+ = QHash<int, QSharedPointer<QPromise<QtAndroidPrivate::PermissionResult>>>;
+Q_GLOBAL_STATIC(PendingPermissionRequestsHash, g_pendingPermissionRequests);
+Q_CONSTINIT static QBasicMutex g_pendingPermissionRequestsMutex;
+
+static int nextRequestCode()
+{
+ Q_CONSTINIT static QBasicAtomicInt counter = Q_BASIC_ATOMIC_INITIALIZER(0);
+ return counter.fetchAndAddRelaxed(1);
+}
+
+/*!
+ \internal
+
+ This function is called when the result of the permission request is available.
+ Once a permission is requested, the result is braodcast by the OS and listened
+ to by QtActivity which passes it to C++ through a native JNI method call.
+ */
+static void sendRequestPermissionsResult(JNIEnv *env, jobject *obj, jint requestCode,
+ jobjectArray permissions, jintArray grantResults)
+{
+ Q_UNUSED(obj);
+
+ QMutexLocker locker(&g_pendingPermissionRequestsMutex);
+ auto it = g_pendingPermissionRequests->constFind(requestCode);
+ if (it == g_pendingPermissionRequests->constEnd()) {
+ qWarning() << "Found no valid pending permission request for request code" << requestCode;
+ return;
+ }
+
+ auto request = *it;
+ g_pendingPermissionRequests->erase(it);
+ locker.unlock();
+
+ const int size = env->GetArrayLength(permissions);
+ std::unique_ptr<jint[]> results(new jint[size]);
+ env->GetIntArrayRegion(grantResults, 0, size, results.get());
+
+ for (int i = 0 ; i < size; ++i) {
+ QtAndroidPrivate::PermissionResult result = resultFromAndroid(results[i]);
+ request->addResult(result, i);
+ }
+
+ QtAndroidPrivate::releaseAndroidDeadlockProtector();
+ request->finish();
+}
+
+QFuture<QtAndroidPrivate::PermissionResult>
+requestPermissionsInternal(const QStringList &permissions)
+{
+ // No mechanism to request permission for SDK version below 23, because
+ // permissions defined in the manifest are granted at install time.
+ if (QtAndroidPrivate::androidSdkVersion() < 23) {
+ QList<QtAndroidPrivate::PermissionResult> result;
+ result.reserve(permissions.size());
+ // ### can we kick off all checkPermission()s, and whenAll() collect results?
+ for (const QString &permission : permissions)
+ result.push_back(QtAndroidPrivate::checkPermission(permission).result());
+ return QtFuture::makeReadyRangeFuture(result);
+ }
+
+ if (!QtAndroidPrivate::acquireAndroidDeadlockProtector())
+ return QtFuture::makeReadyValueFuture(QtAndroidPrivate::Denied);
+
+ QSharedPointer<QPromise<QtAndroidPrivate::PermissionResult>> promise;
+ promise.reset(new QPromise<QtAndroidPrivate::PermissionResult>());
+ QFuture<QtAndroidPrivate::PermissionResult> future = promise->future();
+ promise->start();
+
+ const int requestCode = nextRequestCode();
+ QMutexLocker locker(&g_pendingPermissionRequestsMutex);
+ g_pendingPermissionRequests->insert(requestCode, promise);
+ locker.unlock();
+
+ QNativeInterface::QAndroidApplication::runOnAndroidMainThread([permissions, requestCode] {
+ QJniEnvironment env;
+ jclass clazz = env.findClass("java/lang/String");
+ auto array = env->NewObjectArray(permissions.size(), clazz, nullptr);
+ int index = 0;
+
+ for (auto &perm : permissions)
+ env->SetObjectArrayElement(array, index++, QJniObject::fromString(perm).object());
+
+ QJniObject(QtAndroidPrivate::activity()).callMethod<void>("requestPermissions",
+ "([Ljava/lang/String;I)V",
+ array,
+ requestCode);
+ env->DeleteLocalRef(array);
+ });
+
+ return future;
+}
+
+/*!
+ \preliminary
+ Requests the \a permission and returns a QFuture representing the
+ result of the request.
+
+ \since 6.2
+ \sa checkPermission()
+*/
+QFuture<QtAndroidPrivate::PermissionResult>
+QtAndroidPrivate::requestPermission(const QString &permission)
+{
+ return requestPermissions({permission});
+}
+
+QFuture<QtAndroidPrivate::PermissionResult>
+QtAndroidPrivate::requestPermissions(const QStringList &permissions)
+{
+ // avoid the uneccessary call and response to an empty permission string
+ if (permissions.isEmpty())
+ return QtFuture::makeReadyValueFuture(QtAndroidPrivate::Denied);
+ return requestPermissionsInternal(permissions);
+}
+
+/*!
+ \preliminary
+ Checks whether this process has the named \a permission and returns a QFuture
+ representing the result of the check.
+
+ \since 6.2
+ \sa requestPermission()
+*/
+QFuture<QtAndroidPrivate::PermissionResult>
+QtAndroidPrivate::checkPermission(const QString &permission)
+{
+ QtAndroidPrivate::PermissionResult result = Denied;
+ if (!permission.isEmpty()) {
+ auto res = QJniObject::callStaticMethod<jint>(qtNativeClassName,
+ "checkSelfPermission",
+ "(Ljava/lang/String;)I",
+ QJniObject::fromString(permission).object());
+ result = resultFromAndroid(res);
+ }
+ return QtFuture::makeReadyValueFuture(result);
+}
+
+bool QtAndroidPrivate::registerPermissionNatives(QJniEnvironment &env)
+{
+ if (QtAndroidPrivate::androidSdkVersion() < 23)
+ return true;
+
+ const JNINativeMethod methods[] = {
+ {"sendRequestPermissionsResult", "(I[Ljava/lang/String;[I)V",
+ reinterpret_cast<void *>(sendRequestPermissionsResult)
+ }};
+
+ return env.registerNativeMethods(qtNativeClassName, methods, 1);
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qandroidextras_p.cpp"
diff --git a/src/corelib/platform/android/qandroidextras_p.h b/src/corelib/platform/android/qandroidextras_p.h
new file mode 100644
index 0000000000..efdc6cf74f
--- /dev/null
+++ b/src/corelib/platform/android/qandroidextras_p.h
@@ -0,0 +1,246 @@
+// Copyright (C) 2016 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
+
+#ifndef QANDROIDEXTRAS_H
+#define QANDROIDEXTRAS_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <jni.h>
+#include <functional>
+
+#include <QtCore/private/qglobal_p.h>
+#include <QtCore/qjniobject.h>
+#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qmap.h>
+
+#if QT_CONFIG(future)
+#include <QtCore/qfuture.h>
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QAndroidParcel;
+class QAndroidBinderPrivate;
+class QAndroidBinder;
+
+class Q_CORE_EXPORT QAndroidBinder
+{
+public:
+ enum class CallType {
+ Normal = 0,
+ OneWay = 1
+ };
+
+public:
+ explicit QAndroidBinder();
+ QAndroidBinder(const QJniObject &binder);
+
+ virtual ~QAndroidBinder();
+
+ virtual bool onTransact(int code, const QAndroidParcel &data,
+ const QAndroidParcel &reply, CallType flags);
+ bool transact(int code, const QAndroidParcel &data,
+ QAndroidParcel *reply = nullptr, CallType flags = CallType::Normal) const;
+
+ QJniObject handle() const;
+
+private:
+ friend class QAndroidBinderPrivate;
+ friend class QAndroidParcelPrivate;
+ friend class QAndroidServicePrivate;
+ QSharedPointer<QAndroidBinderPrivate> d;
+};
+
+class QAndroidParcelPrivate;
+
+class Q_CORE_EXPORT QAndroidParcel
+{
+public:
+ QAndroidParcel();
+ explicit QAndroidParcel(const QJniObject& parcel);
+ virtual ~QAndroidParcel();
+
+ void writeData(const QByteArray &data) const;
+ void writeVariant(const QVariant &value) const;
+ void writeBinder(const QAndroidBinder &binder) const;
+ void writeFileDescriptor(int fd) const;
+
+ QByteArray readData() const;
+ QVariant readVariant() const;
+ QAndroidBinder readBinder() const;
+ int readFileDescriptor() const;
+
+ QJniObject handle() const;
+
+private:
+ friend class QAndroidParcelPrivate;
+ friend class QAndroidBinder;
+ QSharedPointer<QAndroidParcelPrivate> d;
+};
+
+class QAndroidActivityResultReceiverPrivate;
+
+class Q_CORE_EXPORT QAndroidActivityResultReceiver
+{
+public:
+ QAndroidActivityResultReceiver();
+ virtual ~QAndroidActivityResultReceiver();
+ virtual void handleActivityResult(int receiverRequestCode, int resultCode,
+ const QJniObject &data) = 0;
+
+private:
+ friend class QAndroidActivityResultReceiverPrivate;
+ Q_DISABLE_COPY(QAndroidActivityResultReceiver)
+
+ QScopedPointer<QAndroidActivityResultReceiverPrivate> d;
+};
+
+class Q_CORE_EXPORT QAndroidServiceConnection
+{
+public:
+ QAndroidServiceConnection();
+ explicit QAndroidServiceConnection(const QJniObject &serviceConnection);
+ virtual ~QAndroidServiceConnection();
+
+ virtual void onServiceConnected(const QString &name,
+ const QAndroidBinder &serviceBinder) = 0;
+ virtual void onServiceDisconnected(const QString &name) = 0;
+
+ QJniObject handle() const;
+private:
+ Q_DISABLE_COPY(QAndroidServiceConnection)
+ QJniObject m_handle;
+};
+
+class Q_CORE_EXPORT QAndroidIntent
+{
+public:
+ QAndroidIntent();
+ virtual ~QAndroidIntent();
+ explicit QAndroidIntent(const QJniObject &intent);
+ explicit QAndroidIntent(const QString &action);
+ explicit QAndroidIntent(const QJniObject &packageContext, const char *className);
+
+ void putExtra(const QString &key, const QByteArray &data);
+ QByteArray extraBytes(const QString &key);
+
+ void putExtra(const QString &key, const QVariant &value);
+ QVariant extraVariant(const QString &key);
+
+ QJniObject handle() const;
+
+private:
+ QJniObject m_handle;
+};
+
+class QAndroidServicePrivate;
+
+class Q_CORE_EXPORT QAndroidService : public QCoreApplication
+{
+ Q_OBJECT
+
+public:
+ QAndroidService(int &argc, char **argv
+#ifndef Q_QDOC
+ , int flags = ApplicationFlags
+#endif
+ );
+ QAndroidService(int &argc, char **argv,
+ const std::function<QAndroidBinder*(const QAndroidIntent &intent)> & binder
+#ifndef Q_QDOC
+ , int flags = ApplicationFlags
+#endif
+ );
+ virtual ~QAndroidService();
+
+ virtual QAndroidBinder* onBind(const QAndroidIntent &intent);
+
+private:
+ friend class QAndroidServicePrivate;
+ Q_DISABLE_COPY(QAndroidService)
+
+ QScopedPointer<QAndroidServicePrivate> d;
+};
+
+class QAndroidActivityCallbackResultReceiver: public QAndroidActivityResultReceiver
+{
+public:
+ QAndroidActivityCallbackResultReceiver();
+ void handleActivityResult(int receiverRequestCode, int resultCode,
+ const QJniObject &intent) override;
+ void registerCallback(int receiverRequestCode,
+ std::function<void(int, int, const QJniObject &)> callbackFunc);
+
+ static QAndroidActivityCallbackResultReceiver *instance();
+private:
+ QMap<int, std::function<void(int, int, const QJniObject &data)>> callbackMap;
+
+ static QAndroidActivityCallbackResultReceiver *s_instance;
+};
+
+namespace QtAndroidPrivate
+{
+ Q_CORE_EXPORT void startIntentSender(const QJniObject &intentSender,
+ int receiverRequestCode,
+ QAndroidActivityResultReceiver *resultReceiver = nullptr);
+ Q_CORE_EXPORT void startActivity(const QJniObject &intent,
+ int receiverRequestCode,
+ QAndroidActivityResultReceiver *resultReceiver = nullptr);
+ Q_CORE_EXPORT void startActivity(const QAndroidIntent &intent,
+ int receiverRequestCode,
+ QAndroidActivityResultReceiver *resultReceiver = nullptr);
+ Q_CORE_EXPORT void startActivity(const QJniObject &intent,
+ int receiverRequestCode,
+ std::function<void(int, int, const QJniObject &data)>
+ callbackFunc);
+
+ enum class BindFlag {
+ None = 0x00000000,
+ AutoCreate = 0x00000001,
+ DebugUnbind = 0x00000002,
+ NotForeground = 0x00000004,
+ AboveClient = 0x00000008,
+ AllowOomManagement = 0x00000010,
+ WaivePriority = 0x00000020,
+ Important = 0x00000040,
+ AdjustWithActivity = 0x00000080,
+ ExternalService = -2147483648 // 0x80000000
+
+ };
+ Q_DECLARE_FLAGS(BindFlags, BindFlag)
+
+ Q_CORE_EXPORT bool bindService(const QAndroidIntent &serviceIntent,
+ const QAndroidServiceConnection &serviceConnection,
+ BindFlags flags = BindFlag::None);
+
+#if QT_CONFIG(future)
+ enum PermissionResult {
+ Undetermined,
+ Authorized,
+ Denied
+ };
+
+ Q_CORE_EXPORT QFuture<QtAndroidPrivate::PermissionResult>
+ requestPermission(const QString &permission);
+ QFuture<QtAndroidPrivate::PermissionResult>
+ requestPermissions(const QStringList &permissions);
+ Q_CORE_EXPORT QFuture<QtAndroidPrivate::PermissionResult>
+ checkPermission(const QString &permission);
+#endif
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QANDROIDEXTRAS_H
diff --git a/src/corelib/platform/android/qandroidnativeinterface.cpp b/src/corelib/platform/android/qandroidnativeinterface.cpp
index 74d21c5d4c..fc3a09c78b 100644
--- a/src/corelib/platform/android/qandroidnativeinterface.cpp
+++ b/src/corelib/platform/android/qandroidnativeinterface.cpp
@@ -1,47 +1,36 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtCore/qcoreapplication.h>
+// Copyright (C) 2021 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
+
+#include <QtCore/qcoreapplication_platform.h>
+
+#include <QtCore/private/qnativeinterface_p.h>
#include <QtCore/private/qjnihelpers_p.h>
+#include <QtCore/qjniobject.h>
+#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
+#include <QtCore/qfuture.h>
+#include <QtCore/qfuturewatcher.h>
+#include <QtCore/qpromise.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qthreadpool.h>
+#include <deque>
+#include <memory>
+#endif
QT_BEGIN_NAMESPACE
+#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
+static const char qtNativeClassName[] = "org/qtproject/qt/android/QtNative";
+
+struct PendingRunnable {
+ std::function<QVariant()> function;
+ std::shared_ptr<QPromise<QVariant>> promise;
+};
+
+using PendingRunnables = std::deque<PendingRunnable>;
+Q_GLOBAL_STATIC(PendingRunnables, g_pendingRunnables);
+Q_CONSTINIT static QBasicMutex g_pendingRunnablesMutex;
+#endif
+
/*!
\class QNativeInterface::QAndroidApplication
\since 6.2
@@ -57,14 +46,14 @@ QT_BEGIN_NAMESPACE
QT_DEFINE_NATIVE_INTERFACE(QAndroidApplication);
/*!
- \fn jobject QNativeInterface::QAndroidApplication::context()
+ \fn QJniObject QNativeInterface::QAndroidApplication::context()
- Returns the Android context as a \c jobject. The context is an \c Activity
+ Returns the Android context as a \c QJniObject. The context is an \c Activity
if the main activity object is valid. Otherwise, the context is a \c Service.
\since 6.2
*/
-jobject QNativeInterface::QAndroidApplication::context()
+QtJniTypes::Context QNativeInterface::QAndroidApplication::context()
{
return QtAndroidPrivate::context();
}
@@ -79,7 +68,176 @@ jobject QNativeInterface::QAndroidApplication::context()
*/
bool QNativeInterface::QAndroidApplication::isActivityContext()
{
- return QtAndroidPrivate::activity();
+ return QtAndroidPrivate::activity().isValid();
+}
+
+/*!
+ \fn int QNativeInterface::QAndroidApplication::sdkVersion()
+
+ Returns the Android SDK version. This is also known as the API level.
+
+ \since 6.2
+*/
+int QNativeInterface::QAndroidApplication::sdkVersion()
+{
+ return QtAndroidPrivate::androidSdkVersion();
+}
+
+/*!
+ \fn void QNativeInterface::QAndroidApplication::hideSplashScreen(int duration)
+
+ Hides the splash screen by using a fade effect for the given \a duration.
+ If \a duration is not provided (default is 0) the splash screen is hidden
+ immedetiately after the app starts.
+
+ \since 6.2
+*/
+void QNativeInterface::QAndroidApplication::hideSplashScreen(int duration)
+{
+ QtAndroidPrivate::activity().callMethod<void>("hideSplashScreen", duration);
+}
+
+/*!
+ Posts the function \a runnable to the Android thread. The function will be
+ queued and executed on the Android UI thread. If the call is made on the
+ Android UI thread \a runnable will be executed immediately. If the Android
+ app is paused or the main Activity is null, \c runnable is added to the
+ Android main thread's queue.
+
+ This call returns a QFuture<QVariant> which allows doing both synchronous
+ and asynchronous calls, and can handle any return type. However, to get
+ a result back from the QFuture::result(), QVariant::value() should be used.
+
+ If the \a runnable execution takes longer than the period of \a timeout,
+ the blocking calls \l QFuture::waitForFinished() and \l QFuture::result()
+ are ended once \a timeout has elapsed. However, if \a runnable has already
+ started execution, it won't be cancelled.
+
+ The following example shows how to run an asynchronous call that expects
+ a return type:
+
+ \code
+ auto task = QNativeInterface::QAndroidApplication::runOnAndroidMainThread([=]() {
+ QJniObject surfaceView;
+ if (!surfaceView.isValid())
+ qDebug() << "SurfaceView object is not valid yet";
+
+ surfaceView = QJniObject("android/view/SurfaceView",
+ "(Landroid/content/Context;)V",
+ QNativeInterface::QAndroidApplication::context());
+
+ return QVariant::fromValue(surfaceView);
+ }).then([](QFuture<QVariant> future) {
+ auto surfaceView = future.result().value<QJniObject>();
+ if (surfaceView.isValid())
+ qDebug() << "Retrieved SurfaceView object is valid";
+ });
+ \endcode
+
+ The following example shows how to run a synchronous call with a void
+ return type:
+
+ \code
+ QNativeInterface::QAndroidApplication::runOnAndroidMainThread([]() {
+ QJniObject activity = QNativeInterface::QAndroidApplication::context();
+ // Hide system ui elements or go full screen
+ activity.callObjectMethod("getWindow", "()Landroid/view/Window;")
+ .callObjectMethod("getDecorView", "()Landroid/view/View;")
+ .callMethod<void>("setSystemUiVisibility", "(I)V", 0xffffffff);
+ }).waitForFinished();
+ \endcode
+
+ \note Becareful about the type of operations you do on the Android's main
+ thread, as any long operation can block the app's UI rendering and input
+ handling. If the function is expected to have long execution time, it's
+ also good to use a \l QDeadlineTimer in your \a runnable to manage
+ the execution and make sure it doesn't block the UI thread. Usually,
+ any operation longer than 5 seconds might block the app's UI. For more
+ information, see \l {Android: Keeping your app responsive}{Keeping your app responsive}.
+
+ \since 6.2
+*/
+#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
+QFuture<QVariant> QNativeInterface::QAndroidApplication::runOnAndroidMainThread(
+ const std::function<QVariant()> &runnable,
+ const QDeadlineTimer timeout)
+{
+ auto promise = std::make_shared<QPromise<QVariant>>();
+ QFuture<QVariant> future = promise->future();
+ promise->start();
+
+ if (!timeout.isForever()) {
+ QThreadPool::globalInstance()->start([=]() mutable {
+ QEventLoop loop;
+ QTimer::singleShot(timeout.remainingTime(), &loop, [&]() {
+ future.cancel();
+ promise->finish();
+ loop.quit();
+ });
+
+ QFutureWatcher<QVariant> watcher;
+ QObject::connect(&watcher, &QFutureWatcher<QVariant>::finished, &loop, [&]() {
+ loop.quit();
+ });
+ QObject::connect(&watcher, &QFutureWatcher<QVariant>::canceled, &loop, [&]() {
+ loop.quit();
+ });
+ watcher.setFuture(future);
+
+ // we're going to sleep, make sure we don't block
+ // QThreadPool::globalInstance():
+
+ QThreadPool::globalInstance()->releaseThread();
+ const auto sg = qScopeGuard([] {
+ QThreadPool::globalInstance()->reserveThread();
+ });
+ loop.exec();
+ });
+ }
+
+ QMutexLocker locker(&g_pendingRunnablesMutex);
+#ifdef __cpp_aggregate_paren_init
+ g_pendingRunnables->emplace_back(runnable, std::move(promise));
+#else
+ g_pendingRunnables->push_back({runnable, std::move(promise)});
+#endif
+ locker.unlock();
+
+ QJniObject::callStaticMethod<void>(qtNativeClassName,
+ "runPendingCppRunnablesOnAndroidThread",
+ "()V");
+ return future;
+}
+
+// function called from Java from Android UI thread
+static void runPendingCppRunnables(JNIEnv */*env*/, jobject /*obj*/)
+{
+ // run all posted runnables
+ for (;;) {
+ QMutexLocker locker(&g_pendingRunnablesMutex);
+ if (g_pendingRunnables->empty())
+ break;
+
+ PendingRunnable r = std::move(g_pendingRunnables->front());
+ g_pendingRunnables->pop_front();
+ locker.unlock();
+
+ // run the runnable outside the sync block!
+ if (!r.promise->isCanceled())
+ r.promise->addResult(r.function());
+ r.promise->finish();
+ }
+}
+#endif
+
+bool QtAndroidPrivate::registerNativeInterfaceNatives(QJniEnvironment &env)
+{
+#if QT_CONFIG(future) && !defined(QT_NO_QOBJECT)
+ const JNINativeMethod methods = {"runPendingCppRunnables", "()V", (void *)runPendingCppRunnables};
+ return env.registerNativeMethods(qtNativeClassName, &methods, 1);
+#else
+ return true;
+#endif
}
QT_END_NAMESPACE