Plumb public permission APIs to Android backend
The lock and unlock of the Android deadlock mutex is now part
of the internal implementation instead of limited to the enum
based permission API. It is unclear why 8bca441b6f
added
the guard only to this API and not to the string based API
as well.
The check for isBackgroundLocationApi29 has been removed,
as the logic seemingly resulted in accepting every single
permission type except location permissions if used via
the enum-based API.
Since Android's platform permission API doesn't have an
Undetermined status, we keep a hash of the status for each
permission type, and by default checkPermission() would
return Undetermined, until a requestPermission() call
is done which updates the internal hash, and after that
checkPermission() would return properly Granted/Denied.
Task-number: QTBUG-100413
Change-Id: Ia95c76af754481a281bc90198e349966c9c2da52
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
parent
64dc886db7
commit
ef935f6e37
@ -12,14 +12,18 @@ set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/corelib/permissions")
|
||||
find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets)
|
||||
|
||||
qt_add_executable(permissions
|
||||
MANUAL_FINALIZATION
|
||||
main.cpp
|
||||
)
|
||||
|
||||
set_target_properties(permissions PROPERTIES
|
||||
MACOSX_BUNDLE TRUE
|
||||
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist"
|
||||
QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android"
|
||||
)
|
||||
|
||||
qt_finalize_executable(permissions)
|
||||
|
||||
target_link_libraries(permissions PUBLIC
|
||||
Qt::Core
|
||||
Qt::Gui
|
||||
|
53
examples/corelib/permissions/android/AndroidManifest.xml
Normal file
53
examples/corelib/permissions/android/AndroidManifest.xml
Normal file
@ -0,0 +1,53 @@
|
||||
<?xml version="1.0"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.qtproject.example"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
|
||||
android:versionName="-- %%INSERT_VERSION_NAME%% --">
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
||||
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
|
||||
<!-- %%INSERT_PERMISSIONS -->
|
||||
<!-- %%INSERT_FEATURES -->
|
||||
<supports-screens
|
||||
android:anyDensity="true"
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true" />
|
||||
<application
|
||||
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
||||
android:hardwareAccelerated="true"
|
||||
android:label="-- %%INSERT_APP_NAME%% --"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:allowNativeHeapPointerTagging="false"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupOnly="false">
|
||||
<activity
|
||||
android:name="org.qtproject.qt.android.bindings.QtActivity"
|
||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
||||
android:label="-- %%INSERT_APP_NAME%% --"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="unspecified"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.extract_android_style"
|
||||
android:value="minimal" />
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
@ -1225,6 +1225,11 @@ if(QT_FEATURE_permissions AND APPLE)
|
||||
)
|
||||
endif()
|
||||
|
||||
qt_internal_extend_target(Core CONDITION QT_FEATURE_permissions AND ANDROID
|
||||
SOURCES
|
||||
kernel/qpermissions_android.cpp
|
||||
)
|
||||
|
||||
#### Keys ignored in scope 171:.:mimetypes:mimetypes/mimetypes.pri:QT_FEATURE_mimetype:
|
||||
# MIME_DATABASE = "mimetypes/mime/packages/freedesktop.org.xml"
|
||||
# OTHER_FILES = "$$MIME_DATABASE"
|
||||
|
@ -971,7 +971,7 @@ qt_feature("permissions" PUBLIC
|
||||
SECTION "Utilities"
|
||||
LABEL "Application permissions"
|
||||
PURPOSE "Provides support for requesting user permission to access restricted data or APIs"
|
||||
CONDITION APPLE
|
||||
CONDITION APPLE OR ANDROID
|
||||
)
|
||||
qt_configure_add_summary_section(NAME "Qt Core")
|
||||
qt_configure_add_summary_entry(ARGS "backtrace")
|
||||
|
@ -3315,6 +3315,13 @@
|
||||
or the permission is known to not be accessible or applicable to applications
|
||||
on the given platform.
|
||||
|
||||
\note On Android, there is no \c Undetermined status by the platform's APIs.
|
||||
Thus, if a permission is denied for an app,
|
||||
\l QCoreApplication::checkPermission() returns \c Undetermined
|
||||
by default until \l QCoreApplication::requestPermission() is called.
|
||||
After that \l QCoreApplication::checkPermission() reports a non \c Undetermined
|
||||
status.
|
||||
|
||||
\since 6.5
|
||||
\sa QCoreApplication::requestPermission(), QCoreApplication::checkPermission(),
|
||||
{Application Permissions}
|
||||
|
@ -280,6 +280,10 @@ QMetaType QPermission::type() const
|
||||
\li Apple
|
||||
\li \l{apple-usage-description}{Usage description}
|
||||
\li \c NSCameraUsageDescription
|
||||
\row
|
||||
\li Android
|
||||
\li \l{android-uses-permission}{\c{uses-permission}}
|
||||
\li \c android.permission.CAMERA
|
||||
\include permissions.qdocinc end-usage-declarations
|
||||
|
||||
\include permissions.qdocinc permission-metadata
|
||||
@ -298,6 +302,10 @@ QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QCameraPermission)
|
||||
\li Apple
|
||||
\li \l{apple-usage-description}{Usage description}
|
||||
\li \c NSMicrophoneUsageDescription
|
||||
\row
|
||||
\li Android
|
||||
\li \l{android-uses-permission}{\c{uses-permission}}
|
||||
\li \c android.permission.RECORD_AUDIO
|
||||
\include permissions.qdocinc end-usage-declarations
|
||||
|
||||
\include permissions.qdocinc permission-metadata
|
||||
@ -316,6 +324,10 @@ QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QMicrophonePermission)
|
||||
\li Apple
|
||||
\li \l{apple-usage-description}{Usage description}
|
||||
\li \c NSBluetoothAlwaysUsageDescription
|
||||
\row
|
||||
\li Android
|
||||
\li \l{android-uses-permission}{\c{uses-permission}}
|
||||
\li \c android.permission.BLUETOOTH
|
||||
\include permissions.qdocinc end-usage-declarations
|
||||
|
||||
\include permissions.qdocinc permission-metadata
|
||||
@ -341,6 +353,17 @@ QT_DEFINE_PERMISSION_SPECIAL_FUNCTIONS(QBluetoothPermission)
|
||||
\li \c NSLocationWhenInUseUsageDescription, and
|
||||
\c NSLocationAlwaysUsageDescription if requesting
|
||||
QLocationPermission::Always
|
||||
\row
|
||||
\li Android
|
||||
\li \l{android-uses-permission}{\c{uses-permission}}
|
||||
\li \list
|
||||
\li \c android.permission.ACCESS_FINE_LOCATION for QLocationPermission::Precise
|
||||
\li \c android.permission.ACCESS_COARSE_LOCATION for QLocationPermission::Approximate
|
||||
\li \c android.permission.ACCESS_BACKGROUND_LOCATION for QLocationPermission::Always
|
||||
\endlist
|
||||
\note QLocationPermission::Always \c uses-permission string has
|
||||
to be combined with one or both of QLocationPermission::Precise
|
||||
and QLocationPermission::Approximate strings.
|
||||
\include permissions.qdocinc end-usage-declarations
|
||||
|
||||
\include permissions.qdocinc permission-metadata
|
||||
@ -425,6 +448,11 @@ QLocationPermission::Availability QLocationPermission::availability() const
|
||||
\li Apple
|
||||
\li \l{apple-usage-description}{Usage description}
|
||||
\li \c NSContactsUsageDescription
|
||||
\row
|
||||
\li Android
|
||||
\li \l{android-uses-permission}{\c{uses-permission}}
|
||||
\li \c android.permission.READ_CONTACTS. \c android.permission.WRITE_CONTACTS if
|
||||
QContactsPermission::isReadOnly() is set to \c false.
|
||||
\include permissions.qdocinc end-usage-declarations
|
||||
|
||||
\include permissions.qdocinc permission-metadata
|
||||
@ -468,6 +496,11 @@ bool QContactsPermission::isReadOnly() const
|
||||
\li Apple
|
||||
\li \l{apple-usage-description}{Usage description}
|
||||
\li \c NSCalendarsUsageDescription
|
||||
\row
|
||||
\li Android
|
||||
\li \l{android-uses-permission}{\c{uses-permission}}
|
||||
\li \c android.permission.READ_CALENDAR. \c android.permission.WRITE_CALENDAR if
|
||||
QContactsPermission::isReadOnly() is set to \c false.
|
||||
\include permissions.qdocinc end-usage-declarations
|
||||
|
||||
\include permissions.qdocinc permission-metadata
|
||||
|
137
src/corelib/kernel/qpermissions_android.cpp
Normal file
137
src/corelib/kernel/qpermissions_android.cpp
Normal file
@ -0,0 +1,137 @@
|
||||
// Copyright (C) 2022 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 "qpermissions.h"
|
||||
#include "qpermissions_p.h"
|
||||
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qfuture.h>
|
||||
#include <QtCore/qhash.h>
|
||||
|
||||
#include "private/qandroidextras_p.h"
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static QStringList nativeLocationPermission(const QLocationPermission &permission)
|
||||
{
|
||||
QStringList nativeLocationPermissionList;
|
||||
const int sdkVersion = QtAndroidPrivate::androidSdkVersion();
|
||||
static QString backgroundLocation = u"android.permission.ACCESS_BACKGROUND_LOCATION"_s;
|
||||
static QString fineLocation = u"android.permission.ACCESS_FINE_LOCATION"_s;
|
||||
static QString coarseLocation = u"android.permission.ACCESS_COARSE_LOCATION"_s;
|
||||
|
||||
// Since Android API 30, background location cannot be requested along
|
||||
// with fine or coarse location, but it should be requested separately after
|
||||
// the latter have been granted, see
|
||||
// https://developer.android.com/training/location/permissions
|
||||
if (sdkVersion < 30 || permission.availability() == QLocationPermission::WhenInUse) {
|
||||
if (permission.accuracy() == QLocationPermission::Approximate) {
|
||||
nativeLocationPermissionList << coarseLocation;
|
||||
} else {
|
||||
nativeLocationPermissionList << fineLocation;
|
||||
// Since Android API 31, if precise location is requested, it's advised
|
||||
// to request both fine and coarse location permissions, see
|
||||
// https://developer.android.com/training/location/permissions#approximate-request
|
||||
if (sdkVersion >= 31)
|
||||
nativeLocationPermissionList << coarseLocation;
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: before Android API 29, background permission doesn't exist yet.
|
||||
|
||||
// Keep the background permission in front to be able to use first()
|
||||
// on the list in checkPermission() because it takes single permission.
|
||||
if (sdkVersion >= 29 && permission.availability() == QLocationPermission::Always)
|
||||
nativeLocationPermissionList.prepend(backgroundLocation);
|
||||
|
||||
return nativeLocationPermissionList;
|
||||
}
|
||||
|
||||
static QStringList nativeStringsFromPermission(const QPermission &permission)
|
||||
{
|
||||
const auto id = permission.type().id();
|
||||
if (id == qMetaTypeId<QLocationPermission>()) {
|
||||
return nativeLocationPermission(permission.data<QLocationPermission>());
|
||||
} else if (id == qMetaTypeId<QCameraPermission>()) {
|
||||
return { u"android.permission.CAMERA"_s };
|
||||
} else if (id == qMetaTypeId<QMicrophonePermission>()) {
|
||||
return { u"android.permission.RECORD_AUDIO"_s };
|
||||
} else if (id == qMetaTypeId<QBluetoothPermission>()) {
|
||||
// TODO: handle Android 12 new bluetooth permissions
|
||||
return { u"android.permission.BLUETOOTH"_s };
|
||||
} else if (id == qMetaTypeId<QContactsPermission>()) {
|
||||
const auto readContactsString = u"android.permission.READ_CONTACTS"_s;
|
||||
if (permission.data<QContactsPermission>().isReadOnly())
|
||||
return { readContactsString };
|
||||
return { readContactsString, u"android.permission.WRITE_CONTACTS"_s };
|
||||
} else if (id == qMetaTypeId<QCalendarPermission>()) {
|
||||
const auto readContactsString = u"android.permission.READ_CALENDAR"_s;
|
||||
if (permission.data<QCalendarPermission>().isReadOnly())
|
||||
return { readContactsString };
|
||||
return { readContactsString, u"android.permission.WRITE_CALENDAR"_s };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
static Qt::PermissionStatus
|
||||
permissionStatusForAndroidResult(QtAndroidPrivate::PermissionResult result)
|
||||
{
|
||||
switch (result) {
|
||||
case QtAndroidPrivate::PermissionResult::Authorized: return Qt::PermissionStatus::Granted;
|
||||
case QtAndroidPrivate::PermissionResult::Denied: return Qt::PermissionStatus::Denied;
|
||||
default: return Qt::PermissionStatus::Undetermined;
|
||||
}
|
||||
}
|
||||
|
||||
using PermissionStatusHash = QHash<int, Qt::PermissionStatus>;
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(PermissionStatusHash, g_permissionStatusHash, ({
|
||||
{ qMetaTypeId<QCameraPermission>(), Qt::PermissionStatus::Undetermined },
|
||||
{ qMetaTypeId<QMicrophonePermission>(), Qt::PermissionStatus::Undetermined },
|
||||
{ qMetaTypeId<QBluetoothPermission>(), Qt::PermissionStatus::Undetermined },
|
||||
{ qMetaTypeId<QContactsPermission>(), Qt::PermissionStatus::Undetermined },
|
||||
{ qMetaTypeId<QCalendarPermission>(), Qt::PermissionStatus::Undetermined },
|
||||
{ qMetaTypeId<QLocationPermission>(), Qt::PermissionStatus::Undetermined }
|
||||
}));
|
||||
|
||||
namespace QPermissions::Private
|
||||
{
|
||||
Qt::PermissionStatus checkPermission(const QPermission &permission)
|
||||
{
|
||||
const auto nativePermissionList = nativeStringsFromPermission(permission);
|
||||
if (nativePermissionList.isEmpty())
|
||||
return Qt::PermissionStatus::Granted;
|
||||
|
||||
const auto result = QtAndroidPrivate::checkPermission(nativePermissionList.first()).result();
|
||||
const auto status = permissionStatusForAndroidResult(result);
|
||||
const auto it = g_permissionStatusHash->constFind(permission.type().id());
|
||||
const bool foundStatus = (it != g_permissionStatusHash->constEnd());
|
||||
const bool itUndetermined = foundStatus && (*it) == Qt::PermissionStatus::Undetermined;
|
||||
if (status == Qt::PermissionStatus::Denied && itUndetermined)
|
||||
return Qt::PermissionStatus::Undetermined;
|
||||
return status;
|
||||
}
|
||||
|
||||
void requestPermission(const QPermission &permission,
|
||||
const QPermissions::Private::PermissionCallback &callback)
|
||||
{
|
||||
const auto nativePermissionList = nativeStringsFromPermission(permission);
|
||||
if (nativePermissionList.isEmpty()) {
|
||||
callback(Qt::PermissionStatus::Granted);
|
||||
return;
|
||||
}
|
||||
|
||||
QtAndroidPrivate::requestPermissions(nativePermissionList).then(qApp,
|
||||
[callback, permission](QFuture<QtAndroidPrivate::PermissionResult> future) {
|
||||
const auto result = future.isValid() ? future.result() : QtAndroidPrivate::Denied;
|
||||
const auto status = permissionStatusForAndroidResult(result);
|
||||
g_permissionStatusHash->insert(permission.type().id(), status);
|
||||
callback(status);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
@ -1030,54 +1030,6 @@ static int nextRequestCode()
|
||||
return counter.fetchAndAddRelaxed(1);
|
||||
}
|
||||
|
||||
static QStringList nativeStringsFromPermission(QtAndroidPrivate::PermissionType permission)
|
||||
{
|
||||
static const auto precisePerm = QStringLiteral("android.permission.ACCESS_FINE_LOCATION");
|
||||
static const auto coarsePerm = QStringLiteral("android.permission.ACCESS_COARSE_LOCATION");
|
||||
static const auto backgroundPerm =
|
||||
QStringLiteral("android.permission.ACCESS_BACKGROUND_LOCATION");
|
||||
|
||||
switch (permission) {
|
||||
case QtAndroidPrivate::Location:
|
||||
return {coarsePerm};
|
||||
case QtAndroidPrivate::PreciseLocation:
|
||||
return {precisePerm};
|
||||
case QtAndroidPrivate::BackgroundLocation:
|
||||
// Keep the background permission first to be able to use .first()
|
||||
// in checkPermission because it takes single permission
|
||||
if (QtAndroidPrivate::androidSdkVersion() >= 29)
|
||||
return {backgroundPerm, coarsePerm};
|
||||
return {coarsePerm};
|
||||
case QtAndroidPrivate::PreciseBackgroundLocation:
|
||||
// Keep the background permission first to be able to use .first()
|
||||
// in checkPermission because it takes single permission
|
||||
if (QtAndroidPrivate::androidSdkVersion() >= 29)
|
||||
return {backgroundPerm, precisePerm};
|
||||
return {precisePerm};
|
||||
case QtAndroidPrivate::Camera:
|
||||
return {QStringLiteral("android.permission.CAMERA")};
|
||||
case QtAndroidPrivate::Microphone:
|
||||
return {QStringLiteral("android.permission.RECORD_AUDIO")};
|
||||
case QtAndroidPrivate::Bluetooth:
|
||||
return { QStringLiteral("android.permission.BLUETOOTH") };
|
||||
case QtAndroidPrivate::BodySensors:
|
||||
return {QStringLiteral("android.permission.BODY_SENSORS")};
|
||||
case QtAndroidPrivate::PhysicalActivity:
|
||||
return {QStringLiteral("android.permission.ACTIVITY_RECOGNITION")};
|
||||
case QtAndroidPrivate::Contacts:
|
||||
return {QStringLiteral("android.permission.READ_CONTACTS"),
|
||||
QStringLiteral("android.permission.WRITE_CONTACTS")};
|
||||
case QtAndroidPrivate::Storage:
|
||||
return {QStringLiteral("android.permission.READ_EXTERNAL_STORAGE"),
|
||||
QStringLiteral("android.permission.WRITE_EXTERNAL_STORAGE")};
|
||||
case QtAndroidPrivate::Calendar:
|
||||
return {QStringLiteral("android.permission.READ_CALENDAR"),
|
||||
QStringLiteral("android.permission.WRITE_CALENDAR")};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
|
||||
@ -1110,6 +1062,7 @@ static void sendRequestPermissionsResult(JNIEnv *env, jobject *obj, jint request
|
||||
request->addResult(result, i);
|
||||
}
|
||||
|
||||
QtAndroidPrivate::releaseAndroidDeadlockProtector();
|
||||
request->finish();
|
||||
}
|
||||
|
||||
@ -1130,6 +1083,12 @@ requestPermissionsInternal(const QStringList &permissions)
|
||||
return future;
|
||||
}
|
||||
|
||||
if (!QtAndroidPrivate::acquireAndroidDeadlockProtector()) {
|
||||
promise->addResult(QtAndroidPrivate::Denied);
|
||||
promise->finish();
|
||||
return future;
|
||||
}
|
||||
|
||||
const int requestCode = nextRequestCode();
|
||||
QMutexLocker locker(&g_pendingPermissionRequestsMutex);
|
||||
g_pendingPermissionRequests->insert(requestCode, promise);
|
||||
@ -1163,10 +1122,16 @@ requestPermissionsInternal(const QStringList &permissions)
|
||||
*/
|
||||
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 (permission.size() > 0)
|
||||
return requestPermissionsInternal({permission});
|
||||
if (permissions.size() > 0)
|
||||
return requestPermissionsInternal(permissions);
|
||||
|
||||
QPromise<QtAndroidPrivate::PermissionResult> promise;
|
||||
QFuture<QtAndroidPrivate::PermissionResult> future = promise.future();
|
||||
@ -1176,55 +1141,6 @@ QtAndroidPrivate::requestPermission(const QString &permission)
|
||||
return future;
|
||||
}
|
||||
|
||||
static bool isBackgroundLocationApi29(QtAndroidPrivate::PermissionType permission)
|
||||
{
|
||||
return QNativeInterface::QAndroidApplication::sdkVersion() >= 29
|
||||
&& (permission == QtAndroidPrivate::BackgroundLocation
|
||||
|| permission == QtAndroidPrivate::PreciseBackgroundLocation);
|
||||
}
|
||||
|
||||
/*!
|
||||
\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(QtAndroidPrivate::PermissionType permission)
|
||||
{
|
||||
QSharedPointer<QPromise<QtAndroidPrivate::PermissionResult>> promise;
|
||||
promise.reset(new QPromise<QtAndroidPrivate::PermissionResult>());
|
||||
QFuture<QtAndroidPrivate::PermissionResult> future = promise->future();
|
||||
promise->start();
|
||||
const auto nativePermissions = nativeStringsFromPermission(permission);
|
||||
|
||||
if (nativePermissions.size() > 0 && QtAndroidPrivate::acquireAndroidDeadlockProtector()) {
|
||||
requestPermissionsInternal(nativePermissions).then(
|
||||
[promise, permission](QFuture<QtAndroidPrivate::PermissionResult> future) {
|
||||
auto AuthorizedCount = future.results().count(QtAndroidPrivate::Authorized);
|
||||
if (AuthorizedCount > 0) {
|
||||
if (isBackgroundLocationApi29(permission))
|
||||
promise->addResult(future.resultAt(0), 0);
|
||||
else
|
||||
promise->addResult(QtAndroidPrivate::Authorized, 0);
|
||||
} else {
|
||||
promise->addResult(QtAndroidPrivate::Denied, 0);
|
||||
}
|
||||
QtAndroidPrivate::releaseAndroidDeadlockProtector();
|
||||
promise->finish();
|
||||
});
|
||||
|
||||
return future;
|
||||
}
|
||||
|
||||
promise->addResult(QtAndroidPrivate::Denied);
|
||||
promise->finish();
|
||||
return future;
|
||||
}
|
||||
|
||||
/*!
|
||||
\preliminary
|
||||
Checks whether this process has the named \a permission and returns a QFuture
|
||||
@ -1254,30 +1170,6 @@ QtAndroidPrivate::checkPermission(const QString &permission)
|
||||
return future;
|
||||
}
|
||||
|
||||
/*!
|
||||
\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(QtAndroidPrivate::PermissionType permission)
|
||||
{
|
||||
const auto nativePermissions = nativeStringsFromPermission(permission);
|
||||
|
||||
if (nativePermissions.size() > 0)
|
||||
return checkPermission(nativePermissions.first());
|
||||
|
||||
QPromise<QtAndroidPrivate::PermissionResult> promise;
|
||||
QFuture<QtAndroidPrivate::PermissionResult> future = promise.future();
|
||||
promise.start();
|
||||
promise.addResult(QtAndroidPrivate::Denied);
|
||||
promise.finish();
|
||||
return future;
|
||||
}
|
||||
|
||||
bool QtAndroidPrivate::registerPermissionNatives()
|
||||
{
|
||||
if (QtAndroidPrivate::androidSdkVersion() < 23)
|
||||
|
@ -225,34 +225,16 @@ namespace QtAndroidPrivate
|
||||
BindFlags flags = BindFlag::None);
|
||||
|
||||
#if QT_CONFIG(future)
|
||||
enum PermissionType {
|
||||
Camera,
|
||||
Microphone,
|
||||
Bluetooth,
|
||||
Location,
|
||||
PreciseLocation,
|
||||
BackgroundLocation,
|
||||
PreciseBackgroundLocation,
|
||||
BodySensors,
|
||||
PhysicalActivity,
|
||||
Contacts,
|
||||
Storage,
|
||||
Calendar
|
||||
};
|
||||
|
||||
enum PermissionResult {
|
||||
Undetermined,
|
||||
Authorized,
|
||||
Denied
|
||||
};
|
||||
|
||||
Q_CORE_EXPORT QFuture<QtAndroidPrivate::PermissionResult>
|
||||
requestPermission(QtAndroidPrivate::PermissionType permission);
|
||||
Q_CORE_EXPORT QFuture<QtAndroidPrivate::PermissionResult>
|
||||
requestPermission(const QString &permission);
|
||||
|
||||
Q_CORE_EXPORT QFuture<QtAndroidPrivate::PermissionResult>
|
||||
checkPermission(QtAndroidPrivate::PermissionType permission);
|
||||
QFuture<QtAndroidPrivate::PermissionResult>
|
||||
requestPermissions(const QStringList &permissions);
|
||||
Q_CORE_EXPORT QFuture<QtAndroidPrivate::PermissionResult>
|
||||
checkPermission(const QString &permission);
|
||||
#endif
|
||||
|
@ -6,7 +6,11 @@ qt_internal_add_test(tst_qpermissions
|
||||
Qt::CorePrivate
|
||||
)
|
||||
|
||||
if (APPLE)
|
||||
if(ANDROID)
|
||||
set_property(TARGET tst_qpermissions
|
||||
PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
|
||||
qt_android_generate_deployment_settings(tst_qpermissions)
|
||||
elseif(APPLE)
|
||||
# Test an app bundle, but without any usage descriptions
|
||||
|
||||
qt_internal_add_test(tst_qpermissions_app
|
||||
|
53
tests/manual/permissions/android/AndroidManifest.xml
Normal file
53
tests/manual/permissions/android/AndroidManifest.xml
Normal file
@ -0,0 +1,53 @@
|
||||
<?xml version="1.0"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.qtproject.example"
|
||||
android:installLocation="auto"
|
||||
android:versionCode="-- %%INSERT_VERSION_CODE%% --"
|
||||
android:versionName="-- %%INSERT_VERSION_NAME%% --">
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||
<uses-permission android:name="android.permission.READ_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
|
||||
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
||||
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
|
||||
<!-- %%INSERT_PERMISSIONS -->
|
||||
<!-- %%INSERT_FEATURES -->
|
||||
<supports-screens
|
||||
android:anyDensity="true"
|
||||
android:largeScreens="true"
|
||||
android:normalScreens="true"
|
||||
android:smallScreens="true" />
|
||||
<application
|
||||
android:name="org.qtproject.qt.android.bindings.QtApplication"
|
||||
android:hardwareAccelerated="true"
|
||||
android:label="-- %%INSERT_APP_NAME%% --"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:allowNativeHeapPointerTagging="false"
|
||||
android:allowBackup="true"
|
||||
android:fullBackupOnly="false">
|
||||
<activity
|
||||
android:name="org.qtproject.qt.android.bindings.QtActivity"
|
||||
android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density"
|
||||
android:label="-- %%INSERT_APP_NAME%% --"
|
||||
android:launchMode="singleTop"
|
||||
android:screenOrientation="unspecified"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.lib_name"
|
||||
android:value="-- %%INSERT_APP_LIB_NAME%% --" />
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.extract_android_style"
|
||||
android:value="minimal" />
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
Loading…
Reference in New Issue
Block a user