diff options
-rw-r--r-- | examples/corelib/permissions/CMakeLists.txt | 4 | ||||
-rw-r--r-- | examples/corelib/permissions/android/AndroidManifest.xml | 53 | ||||
-rw-r--r-- | src/corelib/CMakeLists.txt | 5 | ||||
-rw-r--r-- | src/corelib/configure.cmake | 2 | ||||
-rw-r--r-- | src/corelib/global/qnamespace.qdoc | 7 | ||||
-rw-r--r-- | src/corelib/kernel/qpermissions.cpp | 33 | ||||
-rw-r--r-- | src/corelib/kernel/qpermissions_android.cpp | 137 | ||||
-rw-r--r-- | src/corelib/platform/android/qandroidextras.cpp | 138 | ||||
-rw-r--r-- | src/corelib/platform/android/qandroidextras_p.h | 22 | ||||
-rw-r--r-- | tests/manual/permissions/CMakeLists.txt | 6 | ||||
-rw-r--r-- | tests/manual/permissions/android/AndroidManifest.xml | 53 |
11 files changed, 315 insertions, 145 deletions
diff --git a/examples/corelib/permissions/CMakeLists.txt b/examples/corelib/permissions/CMakeLists.txt index 5c9af5f0d9..7204947ff5 100644 --- a/examples/corelib/permissions/CMakeLists.txt +++ b/examples/corelib/permissions/CMakeLists.txt @@ -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 diff --git a/examples/corelib/permissions/android/AndroidManifest.xml b/examples/corelib/permissions/android/AndroidManifest.xml new file mode 100644 index 0000000000..557ec8007e --- /dev/null +++ b/examples/corelib/permissions/android/AndroidManifest.xml @@ -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> diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index f9dc9f7f20..68444d5ee4 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -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" diff --git a/src/corelib/configure.cmake b/src/corelib/configure.cmake index 0e625408a7..d2e8fa4205 100644 --- a/src/corelib/configure.cmake +++ b/src/corelib/configure.cmake @@ -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") diff --git a/src/corelib/global/qnamespace.qdoc b/src/corelib/global/qnamespace.qdoc index 5414369efe..004899891c 100644 --- a/src/corelib/global/qnamespace.qdoc +++ b/src/corelib/global/qnamespace.qdoc @@ -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} diff --git a/src/corelib/kernel/qpermissions.cpp b/src/corelib/kernel/qpermissions.cpp index be9717694a..4fded99915 100644 --- a/src/corelib/kernel/qpermissions.cpp +++ b/src/corelib/kernel/qpermissions.cpp @@ -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 diff --git a/src/corelib/kernel/qpermissions_android.cpp b/src/corelib/kernel/qpermissions_android.cpp new file mode 100644 index 0000000000..cdafb0144c --- /dev/null +++ b/src/corelib/kernel/qpermissions_android.cpp @@ -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 diff --git a/src/corelib/platform/android/qandroidextras.cpp b/src/corelib/platform/android/qandroidextras.cpp index aa2d849d96..414374d1e0 100644 --- a/src/corelib/platform/android/qandroidextras.cpp +++ b/src/corelib/platform/android/qandroidextras.cpp @@ -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); @@ -1164,9 +1123,15 @@ 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) diff --git a/src/corelib/platform/android/qandroidextras_p.h b/src/corelib/platform/android/qandroidextras_p.h index 00d1f74a47..efdc6cf74f 100644 --- a/src/corelib/platform/android/qandroidextras_p.h +++ b/src/corelib/platform/android/qandroidextras_p.h @@ -225,21 +225,6 @@ 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, @@ -247,12 +232,9 @@ namespace QtAndroidPrivate }; 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 diff --git a/tests/manual/permissions/CMakeLists.txt b/tests/manual/permissions/CMakeLists.txt index 50ec89665f..cac8716129 100644 --- a/tests/manual/permissions/CMakeLists.txt +++ b/tests/manual/permissions/CMakeLists.txt @@ -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 diff --git a/tests/manual/permissions/android/AndroidManifest.xml b/tests/manual/permissions/android/AndroidManifest.xml new file mode 100644 index 0000000000..557ec8007e --- /dev/null +++ b/tests/manual/permissions/android/AndroidManifest.xml @@ -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> |