summaryrefslogtreecommitdiffstats
path: root/src/corelib/platform/android/qandroidextras.cpp
blob: 6dd135150ae30e39d03781cc836958205ab5d435 (plain)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200
// 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(permissions)#include <QtCore/qpromise.h>#endif QT_BEGIN_NAMESPACE using namespace QtJniTypes;class QAndroidParcelPrivate {public:QAndroidParcelPrivate();explicitQAndroidParcelPrivate(const QJniObject& parcel);voidwriteData(const QByteArray &data)const;voidwriteBinder(const QAndroidBinder &binder)const;voidwriteFileDescriptor(int fd)const; QByteArray readData()const;intreadFileDescriptor()const; QAndroidBinder readBinder()const;private:friend class QAndroidBinder;friend class QAndroidParcel; QJniObject handle;};struct FileDescriptor {explicitFileDescriptor(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){}voidQAndroidParcelPrivate::writeData(const QByteArray &data)const{if(data.isEmpty())return; handle.callMethod<void>("writeByteArray", data);}voidQAndroidParcelPrivate::writeBinder(const QAndroidBinder &binder)const{QJniEnvironment().checkAndClearExceptions(); handle.callMethod<void>("writeStrongBinder","(Landroid/os/IBinder;)V", binder.handle().object());}voidQAndroidParcelPrivate::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;}intQAndroidParcelPrivate::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;");returnQAndroidBinder(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. \since 6.2*//*! Creates a new object. */QAndroidParcel::QAndroidParcel():d(newQAndroidParcelPrivate()){}/*! Wraps the \a parcel object. */QAndroidParcel::QAndroidParcel(const QJniObject& parcel):d(newQAndroidParcelPrivate(parcel)){}QAndroidParcel::~QAndroidParcel(){}/*! Writes the provided \a data as a byte array */voidQAndroidParcel::writeData(const QByteArray &data)const{ d->writeData(data);}/*! Writes the provided \a value. The value is converted into a QByteArray before is written. */voidQAndroidParcel::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. */voidQAndroidParcel::writeBinder(const QAndroidBinder &binder)const{ d->writeBinder(binder);}/*! Writes the provided \a fd. */voidQAndroidParcel::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 */intQAndroidParcel::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. \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:explicitQAndroidBinderPrivate(QAndroidBinder *binder):handle("org/qtproject/qt/android/extras/QtAndroidBinder","(J)V",jlong(binder)),m_isQtAndroidBinder(true){QJniEnvironment().checkAndClearExceptions();}explicitQAndroidBinderPrivate(const QJniObject &binder):handle(binder),m_isQtAndroidBinder(false) {};voidsetDeleteListener(conststd::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(newQAndroidBinderPrivate(this)){}/*! Creates a new object from the \a binder Java object. \sa transact */QAndroidBinder::QAndroidBinder(const QJniObject &binder):d(newQAndroidBinderPrivate(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 */boolQAndroidBinder::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 */boolQAndroidBinder::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. \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 intuniqueActivityRequestCode(){constexpr uint ReservedForQtOffset =0x1000;// Reserve all request codes under 0x1000 for Qtconst 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;intglobalRequestCode(int localRequestCode)const{constauto oldSize = localToGlobalRequestCode.size();auto&e = localToGlobalRequestCode[localRequestCode];if(localToGlobalRequestCode.size() != oldSize) {// new entry, populate:int globalRequestCode =uniqueActivityRequestCode(); e = globalRequestCode; globalToLocalRequestCode[globalRequestCode] = localRequestCode;}return e;}boolhandleActivityResult(jint requestCode, jint resultCode, jobject data){constauto 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.get();}};/*! \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. *//*! \internal*/QAndroidActivityResultReceiver::QAndroidActivityResultReceiver():d(new QAndroidActivityResultReceiverPrivate){ d->q =this;QtAndroidPrivate::registerActivityResultListener(d.get());}/*! \internal*/QAndroidActivityResultReceiver::~QAndroidActivityResultReceiver(){QtAndroidPrivate::unregisterActivityResultListener(d.get());}/*! \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,conststd::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();}returnnullptr;}private:voidbinderDestroied(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. \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,conststd::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*/){returnnullptr;}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 voidonServiceConnected(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 voidonServiceDisconnected(JNIEnv */*env*/, jclass /*cls*/, jlong id, jstring name){if(!id)return;return reinterpret_cast<QAndroidServiceConnection *>(id)->onServiceDisconnected(QJniObject(name).toString());}boolQtAndroidPrivate::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. \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 */voidQAndroidIntent::putExtra(const QString &key,const QByteArray &data){ m_handle.callMethod<QtJniTypes::Intent>("putExtra", key, data);}/*! Returns the extra \a key data from the Intent extras */ QByteArray QAndroidIntent::extraBytes(const QString &key){return m_handle.callMethod<QByteArray>("getByteArrayExtra", key);}/*! Sets the \a key with the \a value in the Intent extras. */voidQAndroidIntent::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*//*! \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 of QNativeInterface::QAndroidApplication::context() 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. */voidQtAndroidPrivate::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 of QNativeInterface::QAndroidApplication::context() 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. */voidQtAndroidPrivate::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.*/voidQtAndroidPrivate::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 of QNativeInterface::QAndroidApplication::context() 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.*/voidQtAndroidPrivate::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,// fillInIntent0,// flagsMask0,// flagsValues0);// extraFlags}else{ activity.callMethod<void>("startIntentSender","(Landroid/content/IntentSender;Landroid/content/Intent;III)V", intentSender.object<jobject>(),0,// fillInIntent0,// flagsMask0,// flagsValues0);// 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*/boolQtAndroidPrivate::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(){}voidQAndroidActivityCallbackResultReceiver::handleActivityResult(int receiverRequestCode,int resultCode,const QJniObject &intent){ callbackMap[receiverRequestCode](receiverRequestCode, resultCode, intent); callbackMap.remove(receiverRequestCode);} QAndroidActivityCallbackResultReceiver *QAndroidActivityCallbackResultReceiver::instance() {if(!s_instance) { s_instance =newQAndroidActivityCallbackResultReceiver();}return s_instance;}voidQAndroidActivityCallbackResultReceiver::registerCallback(int receiverRequestCode,std::function<void(int,int,const QJniObject &data)> callbackFunc){ callbackMap.insert(receiverRequestCode, callbackFunc);}#if QT_CONFIG(permissions)// Permissions APIQtAndroidPrivate::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 intnextRequestCode(){ 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 broadcast by the OS and listened to by QtActivity which passes it to C++ through a native JNI method call. */static voidsendRequestPermissionsResult(JNIEnv *env, jclass obj, jint requestCode,const QJniArray<int> &grantResults){Q_UNUSED(env);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(); request->addResults([grantResults](){ QList<QtAndroidPrivate::PermissionResult>results(grantResults.size(),Qt::Uninitialized);for(qsizetype i =0; i < grantResults.size(); ++i) results[i] =resultFromAndroid(grantResults.at(i));return results;}());QtAndroidPrivate::releaseAndroidDeadlockProtector(); request->finish();}Q_DECLARE_JNI_NATIVE_METHOD(sendRequestPermissionsResult) 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());returnQtFuture::makeReadyRangeFuture(result);}if(!QtAndroidPrivate::acquireAndroidDeadlockProtector())returnQtFuture::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);});QtAndroidPrivate::releaseAndroidDeadlockProtector();return future;}/*! \preliminary Requests the \a permission and returns a QFuture representing the result of the request. \note QPermission is the recommended API to use for requesting permissions. If QPermission doesn't cover an Android permission you want to request, this preliminary API can still used instead. \since 6.2 \sa checkPermission()*/ QFuture<QtAndroidPrivate::PermissionResult>QtAndroidPrivate::requestPermission(const QString &permission){returnrequestPermissions({permission});} QFuture<QtAndroidPrivate::PermissionResult>QtAndroidPrivate::requestPermissions(const QStringList &permissions){// avoid the uneccessary call and response to an empty permission stringif(permissions.isEmpty())returnQtFuture::makeReadyValueFuture(QtAndroidPrivate::Denied);returnrequestPermissionsInternal(permissions);}/*! \preliminary Checks whether this process has the named \a permission and returns a QFuture representing the result of the check. \note QPermission is the recommended API to use for requesting permissions. If QPermission doesn't cover an Android permission you want to request, this preliminary API can still used instead. \since 6.2 \sa requestPermission()*/ QFuture<QtAndroidPrivate::PermissionResult>QtAndroidPrivate::checkPermission(const QString &permission){QtAndroidPrivate::PermissionResult result = Denied;if(!permission.isEmpty()) {auto res =QtNative::callStaticMethod<jint>("checkSelfPermission", permission); result =resultFromAndroid(res);}returnQtFuture::makeReadyValueFuture(result);}boolQtAndroidPrivate::registerPermissionNatives(QJniEnvironment &env){if(QtAndroidPrivate::androidSdkVersion() <23)return true;return env.registerNativeMethods<QtNative>({Q_JNI_NATIVE_METHOD(sendRequestPermissionsResult)});}#endif// QT_CONFIG(permissions) QT_END_NAMESPACE #include"moc_qandroidextras_p.cpp"
close