iOS: Update bluetooth permission handling to the new QPermission system
This commit is contained in:
parent
0f8c2a879e
commit
238e86c930
@ -77,16 +77,6 @@ void JsonRpcClient::registerNotificationHandler(QObject *handler, const QString
|
||||
m_notificationHandlers.insert(nameSpace, handler);
|
||||
m_notificationHandlerMethods.insert(handler, method);
|
||||
|
||||
// Clean up if the handler gets destroyed so we don't dereference dangling pointers when
|
||||
// processing notifications.
|
||||
connect(handler, &QObject::destroyed, this, [this](QObject *obj){
|
||||
for (const QString &ns : m_notificationHandlers.keys(obj)) {
|
||||
m_notificationHandlers.remove(ns, obj);
|
||||
}
|
||||
m_notificationHandlerMethods.remove(obj);
|
||||
setNotificationsEnabled();
|
||||
});
|
||||
|
||||
setNotificationsEnabled();
|
||||
}
|
||||
|
||||
|
||||
@ -28,6 +28,8 @@
|
||||
#include <QTimer>
|
||||
#include <QBluetoothLocalDevice>
|
||||
#include <QBluetoothUuid>
|
||||
#include <QBluetoothPermission>
|
||||
#include <QCoreApplication>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcBluetoothDiscovery);
|
||||
@ -64,8 +66,6 @@ BluetoothDiscovery::BluetoothDiscovery(QObject *parent) :
|
||||
#else
|
||||
// Note: on iOS there is no QBluetoothLocalDevice available, therefore we have to assume there is one and
|
||||
// start the discovery agent with the default constructor.
|
||||
// https://bugreports.qt.io/browse/QTBUG-65547
|
||||
|
||||
m_bluetoothAvailable = true;
|
||||
|
||||
// Always start with assuming BT is enabled
|
||||
@ -88,13 +88,16 @@ bool BluetoothDiscovery::bluetoothEnabled() const
|
||||
#ifdef Q_OS_IOS
|
||||
return m_bluetoothAvailable && m_bluetoothEnabled;
|
||||
#endif
|
||||
|
||||
qCDebug(dcBluetoothDiscovery) << "bluetoothEnabled(): m_bluetoothAvailable:" << m_bluetoothAvailable;
|
||||
return m_bluetoothAvailable && m_localDevice->hostMode() != QBluetoothLocalDevice::HostPoweredOff;
|
||||
}
|
||||
void BluetoothDiscovery::setBluetoothEnabled(bool bluetoothEnabled) {
|
||||
if (!m_bluetoothAvailable) {
|
||||
|
||||
void BluetoothDiscovery::setBluetoothEnabled(bool bluetoothEnabled)
|
||||
{
|
||||
if (!m_bluetoothAvailable)
|
||||
return;
|
||||
}
|
||||
|
||||
if (bluetoothEnabled) {
|
||||
if (m_localDevice->hostMode() == QBluetoothLocalDevice::HostPoweredOff) {
|
||||
m_localDevice->powerOn();
|
||||
@ -151,26 +154,30 @@ void BluetoothDiscovery::onBluetoothHostModeChanged(const QBluetoothLocalDevice:
|
||||
#endif
|
||||
emit bluetoothEnabledChanged(false);
|
||||
break;
|
||||
|
||||
default:
|
||||
// Note: discovery works in all other modes
|
||||
#ifdef Q_OS_IOS
|
||||
m_bluetoothEnabled = true;
|
||||
#endif
|
||||
emit bluetoothEnabledChanged(hostMode != QBluetoothLocalDevice::HostPoweredOff);
|
||||
|
||||
if (!m_discoveryAgent) {
|
||||
#ifdef Q_OS_ANDROID
|
||||
m_discoveryAgent = new QBluetoothDeviceDiscoveryAgent(m_localDevice->address(), this);
|
||||
#else
|
||||
m_discoveryAgent = new QBluetoothDeviceDiscoveryAgent(this);
|
||||
#endif
|
||||
connect(m_discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BluetoothDiscovery::deviceDiscovered);
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
|
||||
connect(m_discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceUpdated, this, &BluetoothDiscovery::deviceDiscovered);
|
||||
#elif (QT_VERSION >= QT_VERSION_CHECK(6, 2, 0))
|
||||
#endif
|
||||
|
||||
#if (QT_VERSION >= QT_VERSION_CHECK(6, 2, 0))
|
||||
connect(m_discoveryAgent, &QBluetoothDeviceDiscoveryAgent::errorOccurred, this, &BluetoothDiscovery::onError);
|
||||
#else
|
||||
connect(m_discoveryAgent, SIGNAL(error(QBluetoothDeviceDiscoveryAgent::Error)), this, SLOT(onError(QBluetoothDeviceDiscoveryAgent::Error)));
|
||||
#endif
|
||||
connect(m_discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered, this, &BluetoothDiscovery::deviceDiscovered);
|
||||
connect(m_discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished, this, &BluetoothDiscovery::discoveryFinished);
|
||||
connect(m_discoveryAgent, &QBluetoothDeviceDiscoveryAgent::canceled, this, &BluetoothDiscovery::discoveryCancelled);
|
||||
}
|
||||
@ -241,13 +248,11 @@ void BluetoothDiscovery::onError(const QBluetoothDeviceDiscoveryAgent::Error &er
|
||||
|
||||
void BluetoothDiscovery::start()
|
||||
{
|
||||
if (!m_discoveryAgent || !bluetoothEnabled()) {
|
||||
if (!m_discoveryAgent || !bluetoothEnabled())
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_discoveryAgent->isActive()) {
|
||||
if (m_discoveryAgent->isActive())
|
||||
m_discoveryAgent->stop();
|
||||
}
|
||||
|
||||
foreach (const QBluetoothDeviceInfo &info, m_discoveryAgent->discoveredDevices()) {
|
||||
qCDebug(dcBluetoothDiscovery()) << "Already discovered device:" << info.name();
|
||||
@ -255,7 +260,9 @@ void BluetoothDiscovery::start()
|
||||
}
|
||||
|
||||
qCDebug(dcBluetoothDiscovery) << "Starting discovery.";
|
||||
m_discoveryAgent->start();
|
||||
|
||||
// Since we are only interested in low energy results, this speed up the result significantly
|
||||
m_discoveryAgent->start(QBluetoothDeviceDiscoveryAgent::LowEnergyMethod);
|
||||
emit discoveringChanged();
|
||||
}
|
||||
|
||||
|
||||
@ -176,9 +176,8 @@ ios: {
|
||||
OTHER_FILES += $${OBJECTIVE_SOURCES}
|
||||
|
||||
LIBS += -framework CoreLocation \
|
||||
-framework CoreBluetooth \
|
||||
-framework CoreNFC
|
||||
|
||||
-framework CoreBluetooth \
|
||||
-framework CoreNFC
|
||||
|
||||
# Add Firebase SDK
|
||||
QMAKE_LFLAGS += -ObjC $(inherited)
|
||||
@ -186,8 +185,10 @@ ios: {
|
||||
firebase_files.files += $$files($${IOS_PACKAGE_DIR}/GoogleService-Info.plist)
|
||||
QMAKE_BUNDLE_DATA += firebase_files
|
||||
INCLUDEPATH += ../3rdParty/ios/
|
||||
|
||||
LIBS += -F$$PWD/../3rdParty/ios/Firebase/FirebaseAnalytics/ \
|
||||
-F$$PWD/../3rdParty/ios/Firebase/FirebaseMessaging
|
||||
|
||||
LIBS += -framework "FirebaseMessaging" \
|
||||
-framework "GoogleUtilities" \
|
||||
-framework "Protobuf" \
|
||||
@ -196,13 +197,13 @@ ios: {
|
||||
-framework "FirebaseInstallations" \
|
||||
-framework "PromisesObjC" \
|
||||
|
||||
|
||||
LIBS += -L$$top_builddir/libnymea-app -lnymea-app \
|
||||
-L$$top_builddir/experiences/airconditioning -lnymea-app-airconditioning \
|
||||
-L$$top_builddir/experiences/airconditioning -lnymea-app-airconditioning \
|
||||
-L$$top_builddir/experiences/evdash -lnymea-app-evdash
|
||||
|
||||
PRE_TARGETDEPS += $$top_builddir/libnymea-app/libnymea-app.a \
|
||||
$$top_builddir/experiences/airconditioning/libnymea-app-airconditioning.a
|
||||
|
||||
$$top_builddir/experiences/airconditioning/libnymea-app-airconditioning.a \
|
||||
$$top_builddir/experiences/evdash/libnymea-app-evdash.a
|
||||
|
||||
# Configure generated xcode project to have our bundle id
|
||||
QMAKE_TARGET_BUNDLE_PREFIX=$${IOS_BUNDLE_PREFIX}
|
||||
@ -220,6 +221,9 @@ ios: {
|
||||
ios_launch_images.files += $${IOS_PACKAGE_DIR}/NymeaLaunchScreen.storyboard
|
||||
QMAKE_BUNDLE_DATA += ios_launch_images
|
||||
|
||||
DEFINES += QT_STATICPLUGIN
|
||||
QTPLUGIN += qdarwinbluetoothpermission
|
||||
|
||||
IOS_DEVELOPMENT_TEAM.name = DEVELOPMENT_TEAM
|
||||
IOS_DEVELOPMENT_TEAM.value = $$IOS_TEAM_ID
|
||||
QMAKE_MAC_XCODE_SETTINGS += IOS_DEVELOPMENT_TEAM
|
||||
|
||||
@ -26,6 +26,11 @@
|
||||
|
||||
#include <QSettings>
|
||||
#include <QApplication>
|
||||
#include <QPermission>
|
||||
#include <QBluetoothPermission>
|
||||
|
||||
#include "logging.h"
|
||||
NYMEA_LOGGING_CATEGORY(dcPlatformPermissions, "PlatformPermissions")
|
||||
|
||||
PlatformPermissionsIOS *PlatformPermissionsIOS::s_instance = nullptr;
|
||||
|
||||
@ -61,13 +66,13 @@ PlatformPermissions::PermissionStatus PlatformPermissionsIOS::checkPermission(Pe
|
||||
case PermissionBluetooth:
|
||||
return checkBluetoothPermission();
|
||||
default:
|
||||
return PermissionStatusGranted;
|
||||
return PermissionStatusGranted;
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformPermissionsIOS::requestPermission(Permission permission)
|
||||
void PlatformPermissionsIOS::requestPermission(Permission platformPermission)
|
||||
{
|
||||
switch (permission) {
|
||||
switch (platformPermission) {
|
||||
case PermissionNone:
|
||||
break;
|
||||
case PermissionLocalNetwork:
|
||||
|
||||
@ -32,9 +32,11 @@
|
||||
#if __OBJC__
|
||||
@class CLLocationManager;
|
||||
@class CBCentralManager;
|
||||
@class BluetoothManagerDelegate;
|
||||
#else
|
||||
typedef void CLLocationManager;
|
||||
typedef void CBCentralManager;
|
||||
typedef void BluetoothManagerDelegate;
|
||||
#endif
|
||||
|
||||
class PlatformPermissionsIOS : public PlatformPermissions
|
||||
@ -44,8 +46,8 @@ public:
|
||||
explicit PlatformPermissionsIOS(QObject *parent = nullptr);
|
||||
static PlatformPermissionsIOS *instance();
|
||||
|
||||
PermissionStatus checkPermission(Permission permission) const override;
|
||||
void requestPermission(Permission permission) override;
|
||||
PermissionStatus checkPermission(Permission ) const override;
|
||||
void requestPermission(Permission platformPermission) override;
|
||||
void openPermissionSettings() override;
|
||||
|
||||
private:
|
||||
@ -62,14 +64,15 @@ private:
|
||||
void requestLocalNetworkPermission();
|
||||
void requestNotificationPermission();
|
||||
void requestBluetoothPermission();
|
||||
void requestBluetoothPermissionLegacy();
|
||||
void requestLocationPermission();
|
||||
void requestBackgroundLocationPermission();
|
||||
|
||||
PermissionStatus m_notificationPermissions = PermissionStatusNotDetermined;
|
||||
|
||||
|
||||
CLLocationManager *m_locationManager = nullptr;
|
||||
CBCentralManager *m_bluetoothManager = nullptr;
|
||||
BluetoothManagerDelegate *m_bluetoothDelegate = nullptr;
|
||||
};
|
||||
|
||||
#endif // PLATFORMPERMISSIONSIOS_H
|
||||
|
||||
@ -1,11 +1,25 @@
|
||||
#include "platformpermissionsios.h"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QBluetoothPermission>
|
||||
#include <QPermission>
|
||||
#include <QSharedPointer>
|
||||
#include <QTimer>
|
||||
#include <QtPlugin>
|
||||
|
||||
#import <UserNotifications/UNUserNotificationCenter.h>
|
||||
#import <UserNotifications/UNNotificationSettings.h>
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
#import <CoreBluetooth/CoreBluetooth.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#include "logging.h"
|
||||
Q_DECLARE_LOGGING_CATEGORY(dcPlatformPermissions)
|
||||
|
||||
#ifdef QT_STATICPLUGIN
|
||||
Q_IMPORT_PLUGIN(QDarwinBluetoothPermissionPlugin)
|
||||
#endif
|
||||
|
||||
@interface LocationManagerPermissionDelegate : NSObject <CLLocationManagerDelegate>
|
||||
@end
|
||||
@implementation LocationManagerPermissionDelegate
|
||||
@ -69,28 +83,112 @@ void PlatformPermissionsIOS::requestNotificationPermission()
|
||||
|
||||
PlatformPermissions::PermissionStatus PlatformPermissionsIOS::checkBluetoothPermission() const
|
||||
{
|
||||
// iOS 13.0 would have an api but it's more complicated and also deprecated... Ignoring...
|
||||
qCDebug(dcPlatformPermissions()) << "Checking bluetooth permission...";
|
||||
QBluetoothPermission btPermission;
|
||||
btPermission.setCommunicationModes(QBluetoothPermission::Access);
|
||||
const auto qtStatus = qGuiApp->checkPermission(btPermission);
|
||||
if (qtStatus == Qt::PermissionStatus::Granted) {
|
||||
qCDebug(dcPlatformPermissions()) << "Bluetooth permisson granted (Qt plugin)";
|
||||
return PermissionStatusGranted;
|
||||
} else {
|
||||
qCDebug(dcPlatformPermissions()) << "Bluetooth permisson NOT granted (Qt plugin)";
|
||||
}
|
||||
|
||||
PermissionStatus fallbackStatus = PermissionStatusGranted;
|
||||
if (@available(iOS 13.1, *)) {
|
||||
switch (CBCentralManager.authorization) {
|
||||
case CBManagerAuthorizationAllowedAlways:
|
||||
fallbackStatus = PermissionStatusGranted;
|
||||
break;
|
||||
case CBManagerAuthorizationRestricted:
|
||||
return PermissionStatusGranted;
|
||||
fallbackStatus = PermissionStatusGranted;
|
||||
break;
|
||||
case CBManagerAuthorizationDenied:
|
||||
return PermissionStatusDenied;
|
||||
fallbackStatus = PermissionStatusDenied;
|
||||
break;
|
||||
case CBManagerAuthorizationNotDetermined:
|
||||
return PermissionStatusNotDetermined;
|
||||
fallbackStatus = PermissionStatusNotDetermined;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// Before iOS 13, Bluetooth permissions are not required
|
||||
fallbackStatus = PermissionStatusGranted;
|
||||
}
|
||||
// Before iOS 13, Bluetooth permissions are not required
|
||||
return PermissionStatusGranted;
|
||||
|
||||
switch (qtStatus) {
|
||||
case Qt::PermissionStatus::Denied:
|
||||
qCWarning(dcPlatformPermissions()) << "Bluetooth permission denied by Qt plugin, fallback reports" << fallbackStatus;
|
||||
break;
|
||||
case Qt::PermissionStatus::Undetermined:
|
||||
qCWarning(dcPlatformPermissions()) << "QBluetoothPermission status Undetermined...using fallback.";
|
||||
break;
|
||||
case Qt::PermissionStatus::Granted:
|
||||
break;
|
||||
}
|
||||
|
||||
return fallbackStatus;
|
||||
}
|
||||
|
||||
void PlatformPermissionsIOS::requestBluetoothPermission()
|
||||
{
|
||||
// Instantiating a Bluetooth manager just trigger the popup...
|
||||
qCDebug(dcPlatformPermissions()) << "Requesting bluetooth permission...";
|
||||
auto handlePermissionResult = [](const QPermission &permission) {
|
||||
switch (permission.status()) {
|
||||
case Qt::PermissionStatus::Granted:
|
||||
qCDebug(dcPlatformPermissions()) << "Bluetooth permission granted.";
|
||||
emit s_instance->bluetoothPermissionChanged();
|
||||
return;
|
||||
case Qt::PermissionStatus::Denied:
|
||||
if (s_instance->checkBluetoothPermission() == PermissionStatusNotDetermined) {
|
||||
qCWarning(dcPlatformPermissions()) << "Bluetooth permission plugin unavailable, falling back to CoreBluetooth request.";
|
||||
s_instance->requestBluetoothPermissionLegacy();
|
||||
return;
|
||||
}
|
||||
qCWarning(dcPlatformPermissions()) << "Bluetooth permission denied.";
|
||||
emit s_instance->bluetoothPermissionChanged();
|
||||
return;
|
||||
case Qt::PermissionStatus::Undetermined:
|
||||
qCWarning(dcPlatformPermissions()) << "Bluetooth permission plugin unavailable, falling back to CoreBluetooth request.";
|
||||
s_instance->requestBluetoothPermissionLegacy();
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
QBluetoothPermission btPermission;
|
||||
btPermission.setCommunicationModes(QBluetoothPermission::Access);
|
||||
|
||||
if (qApp->checkPermission(btPermission) == Qt::PermissionStatus::Undetermined) {
|
||||
auto permissionHandled = QSharedPointer<bool>::create(false);
|
||||
|
||||
qApp->requestPermission(btPermission, [handlePermissionResult, permissionHandled](const QPermission &permission) {
|
||||
*permissionHandled = true;
|
||||
handlePermissionResult(permission);
|
||||
});
|
||||
|
||||
// The Qt permission plugin might be missing from certain builds. If we still don't have
|
||||
// a decision after giving it a moment, fall back to the CoreBluetooth prompt.
|
||||
QTimer::singleShot(2000, this, [this, permissionHandled]() {
|
||||
if (*permissionHandled) {
|
||||
return;
|
||||
}
|
||||
if (checkBluetoothPermission() == PermissionStatusNotDetermined) {
|
||||
qCWarning(dcPlatformPermissions()) << "Bluetooth permission plugin unavailable, falling back to CoreBluetooth request.";
|
||||
requestBluetoothPermissionLegacy();
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
handlePermissionResult(btPermission);
|
||||
}
|
||||
|
||||
void PlatformPermissionsIOS::requestBluetoothPermissionLegacy()
|
||||
{
|
||||
qCDebug(dcPlatformPermissions()) << "Requesting bluetooth permission legacy...";
|
||||
// Instantiating a Bluetooth manager triggers the native dialog on first use.
|
||||
if (!m_bluetoothManager) {
|
||||
BluetoothManagerDelegate *delegate = [[BluetoothManagerDelegate alloc] init];
|
||||
m_bluetoothManager = [[CBCentralManager alloc] initWithDelegate:delegate queue:nil];
|
||||
m_bluetoothDelegate = [[BluetoothManagerDelegate alloc] init];
|
||||
m_bluetoothManager = [[CBCentralManager alloc] initWithDelegate:m_bluetoothDelegate queue:nil];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -48,6 +48,14 @@
|
||||
<string>_jsonrpc._tcp</string>
|
||||
<string>_ws._tcp</string>
|
||||
</array>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<!-- Fallback: allow all loads, including self-signed LAN TLS. -->
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
<key>XSAppIconAssets</key>
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
1.11.1
|
||||
687
|
||||
689
|
||||
|
||||
Reference in New Issue
Block a user