summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qpermissions_android.cpp
blob: ded6c7ec8f0a4f51c63de14ba8ba8d3b11a37ed1 (plain)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
// 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" QT_BEGIN_NAMESPACE using namespaceQt::StringLiterals;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/permissionsif(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-requestif(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 nativeBluetoothPermission(const QBluetoothPermission &permission){// See https://developer.android.com/guide/topics/connectivity/bluetooth/permissions// for the details.// API Level < 31static QString bluetoothGeneral = u"android.permission.BLUETOOTH"_s;static QString fineLocation = u"android.permission.ACCESS_FINE_LOCATION"_s;// API Level >= 31static QString bluetoothScan = u"android.permission.BLUETOOTH_SCAN"_s;static QString bluetoothAdvertise = u"android.permission.BLUETOOTH_ADVERTISE"_s;static QString bluetoothConnect = u"android.permission.BLUETOOTH_CONNECT"_s;if(QtAndroidPrivate::androidSdkVersion() <31) {return{bluetoothGeneral, fineLocation};}else{constauto modes = permission.communicationModes(); QStringList permissionList;if(modes &QBluetoothPermission::Advertise) permissionList << bluetoothAdvertise;if(modes &QBluetoothPermission::Access) permissionList << bluetoothScan << bluetoothConnect;return permissionList;}}static QStringList nativeStringsFromPermission(const QPermission &permission){constauto id = permission.type().id();if(id == qMetaTypeId<QLocationPermission>()) {returnnativeLocationPermission(*permission.value<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>()) {returnnativeBluetoothPermission(*permission.value<QBluetoothPermission>());}else if(id == qMetaTypeId<QContactsPermission>()) {constauto readContactsString = u"android.permission.READ_CONTACTS"_s;switch(permission.value<QContactsPermission>()->accessMode()) {caseQContactsPermission::AccessMode::ReadOnly:return{ readContactsString };caseQContactsPermission::AccessMode::ReadWrite:return{ readContactsString, u"android.permission.WRITE_CONTACTS"_s };}Q_UNREACHABLE_RETURN({});}else if(id == qMetaTypeId<QCalendarPermission>()) {constauto readContactsString = u"android.permission.READ_CALENDAR"_s;switch(permission.value<QCalendarPermission>()->accessMode()) {caseQCalendarPermission::AccessMode::ReadOnly:return{ readContactsString };caseQCalendarPermission::AccessMode::ReadWrite:return{ readContactsString, u"android.permission.WRITE_CALENDAR"_s };}Q_UNREACHABLE_RETURN({});}return{};}staticQt::PermissionStatus permissionStatusForAndroidResult(QtAndroidPrivate::PermissionResult result){switch(result) {caseQtAndroidPrivate::PermissionResult::Authorized:returnQt::PermissionStatus::Granted;caseQtAndroidPrivate::PermissionResult::Denied:returnQt::PermissionStatus::Denied;default:returnQt::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 }}));staticQt::PermissionStatus getCombinedStatus(const QList<QtAndroidPrivate::PermissionResult> &androidResults){// Android returns only Denied or Grantedfor(constauto&result : androidResults) {constauto status =permissionStatusForAndroidResult(result);if(status ==Qt::PermissionStatus::Denied)return status;}returnQt::PermissionStatus::Granted;}namespaceQPermissions::Private {Qt::PermissionStatus checkPermission(const QPermission &permission){constauto nativePermissionList =nativeStringsFromPermission(permission);if(nativePermissionList.isEmpty())returnQt::PermissionStatus::Granted; QList<QtAndroidPrivate::PermissionResult> androidResults; androidResults.reserve(nativePermissionList.size());for(constauto&nativePermission : nativePermissionList) androidResults.push_back(QtAndroidPrivate::checkPermission(nativePermission).result());constauto status =getCombinedStatus(androidResults);constauto 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)returnQt::PermissionStatus::Undetermined;return status;}voidrequestPermission(const QPermission &permission,constQPermissions::Private::PermissionCallback &callback){constauto nativePermissionList =nativeStringsFromPermission(permission);if(nativePermissionList.isEmpty()) {callback(Qt::PermissionStatus::Granted);return;}QtAndroidPrivate::requestPermissions(nativePermissionList).then(qApp,[callback, permission](QFuture<QtAndroidPrivate::PermissionResult> future) {constauto androidResults = future.isValid() ? future.results(): QList{QtAndroidPrivate::Denied};constauto status =getCombinedStatus(androidResults); g_permissionStatusHash->insert(permission.type().id(), status);callback(status);});}} QT_END_NAMESPACE 
close