Implement hasPermissions API (#201)
* Implement hasPermissions api * Bump minimum MacOS version to 10.15 * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -1,8 +1,11 @@
|
|||||||
## 1.0.0
|
## 1.0.0
|
||||||
* BREAKING CHANGE: `OnValueChange` callback also includes timestamp
|
* BREAKING CHANGE: `OnValueChange` callback also includes timestamp
|
||||||
|
* BREAKING CHANGE: Bump minimum IOS version to 13.1
|
||||||
|
* BREAKING CHANGE: Bump minimum MacOS version to 10.15
|
||||||
* Fix Windows 11 crash on cancelling pairing
|
* Fix Windows 11 crash on cancelling pairing
|
||||||
* Unified error codes for all platforms
|
* Unified error codes for all platforms
|
||||||
* Add `isScanning` api
|
* Add `isScanning` api
|
||||||
|
* Add `hasPermissions` api
|
||||||
* Add `requestPermissions` api and auto ask permission on `startScan`
|
* Add `requestPermissions` api and auto ask permission on `startScan`
|
||||||
* `disconnect` now waits for disconnection confirmation before returning
|
* `disconnect` now waits for disconnection confirmation before returning
|
||||||
* Improve Windows disconnection event handling and cleanup
|
* Improve Windows disconnection event handling and cleanup
|
||||||
|
|||||||
@@ -24,6 +24,18 @@ class PermissionHandler(
|
|||||||
) {
|
) {
|
||||||
private var permissionRequestCallback: ((Result<Unit>) -> Unit)? = null
|
private var permissionRequestCallback: ((Result<Unit>) -> Unit)? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if we have required permissions
|
||||||
|
*/
|
||||||
|
fun hasPermissions(withFineLocation: Boolean): Boolean {
|
||||||
|
val validationError = validateRequiredPermissions(withFineLocation)
|
||||||
|
if (validationError != null) {
|
||||||
|
throw validationError
|
||||||
|
}
|
||||||
|
val permissionsToRequest = getRequiredPermissions(withFineLocation)
|
||||||
|
return permissionsToRequest.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests the required Bluetooth permissions based on the manifest and Android version.
|
* Requests the required Bluetooth permissions based on the manifest and Android version.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -510,6 +510,7 @@ private open class UniversalBlePigeonCodec : StandardMessageCodec() {
|
|||||||
*/
|
*/
|
||||||
interface UniversalBlePlatformChannel {
|
interface UniversalBlePlatformChannel {
|
||||||
fun getBluetoothAvailabilityState(callback: (Result<Long>) -> Unit)
|
fun getBluetoothAvailabilityState(callback: (Result<Long>) -> Unit)
|
||||||
|
fun hasPermissions(withAndroidFineLocation: Boolean): Boolean
|
||||||
fun requestPermissions(withAndroidFineLocation: Boolean, callback: (Result<Unit>) -> Unit)
|
fun requestPermissions(withAndroidFineLocation: Boolean, callback: (Result<Unit>) -> Unit)
|
||||||
fun enableBluetooth(callback: (Result<Boolean>) -> Unit)
|
fun enableBluetooth(callback: (Result<Boolean>) -> Unit)
|
||||||
fun disableBluetooth(callback: (Result<Boolean>) -> Unit)
|
fun disableBluetooth(callback: (Result<Boolean>) -> Unit)
|
||||||
@@ -557,6 +558,23 @@ interface UniversalBlePlatformChannel {
|
|||||||
channel.setMessageHandler(null)
|
channel.setMessageHandler(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
run {
|
||||||
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.hasPermissions$separatedMessageChannelSuffix", codec)
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler { message, reply ->
|
||||||
|
val args = message as List<Any?>
|
||||||
|
val withAndroidFineLocationArg = args[0] as Boolean
|
||||||
|
val wrapped: List<Any?> = try {
|
||||||
|
listOf(api.hasPermissions(withAndroidFineLocationArg))
|
||||||
|
} catch (exception: Throwable) {
|
||||||
|
UniversalBlePigeonUtils.wrapError(exception)
|
||||||
|
}
|
||||||
|
reply.reply(wrapped)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
run {
|
run {
|
||||||
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestPermissions$separatedMessageChannelSuffix", codec)
|
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestPermissions$separatedMessageChannelSuffix", codec)
|
||||||
if (api != null) {
|
if (api != null) {
|
||||||
|
|||||||
@@ -95,6 +95,10 @@ class UniversalBlePlugin : UniversalBlePlatformChannel, BluetoothGattCallback(),
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun hasPermissions(withAndroidFineLocation: Boolean): Boolean {
|
||||||
|
return permissionHandler?.hasPermissions(withAndroidFineLocation) ?: false
|
||||||
|
}
|
||||||
|
|
||||||
override fun requestPermissions(
|
override fun requestPermissions(
|
||||||
withAndroidFineLocation: Boolean,
|
withAndroidFineLocation: Boolean,
|
||||||
callback: (Result<Unit>) -> Unit,
|
callback: (Result<Unit>) -> Unit,
|
||||||
|
|||||||
@@ -529,6 +529,7 @@ class UniversalBlePigeonCodec: FlutterStandardMessageCodec, @unchecked Sendable
|
|||||||
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
|
/// Generated protocol from Pigeon that represents a handler of messages from Flutter.
|
||||||
protocol UniversalBlePlatformChannel {
|
protocol UniversalBlePlatformChannel {
|
||||||
func getBluetoothAvailabilityState(completion: @escaping (Result<Int64, Error>) -> Void)
|
func getBluetoothAvailabilityState(completion: @escaping (Result<Int64, Error>) -> Void)
|
||||||
|
func hasPermissions(withAndroidFineLocation: Bool) throws -> Bool
|
||||||
func requestPermissions(withAndroidFineLocation: Bool, completion: @escaping (Result<Void, Error>) -> Void)
|
func requestPermissions(withAndroidFineLocation: Bool, completion: @escaping (Result<Void, Error>) -> Void)
|
||||||
func enableBluetooth(completion: @escaping (Result<Bool, Error>) -> Void)
|
func enableBluetooth(completion: @escaping (Result<Bool, Error>) -> Void)
|
||||||
func disableBluetooth(completion: @escaping (Result<Bool, Error>) -> Void)
|
func disableBluetooth(completion: @escaping (Result<Bool, Error>) -> Void)
|
||||||
@@ -571,6 +572,21 @@ class UniversalBlePlatformChannelSetup {
|
|||||||
} else {
|
} else {
|
||||||
getBluetoothAvailabilityStateChannel.setMessageHandler(nil)
|
getBluetoothAvailabilityStateChannel.setMessageHandler(nil)
|
||||||
}
|
}
|
||||||
|
let hasPermissionsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.hasPermissions\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||||
|
if let api = api {
|
||||||
|
hasPermissionsChannel.setMessageHandler { message, reply in
|
||||||
|
let args = message as! [Any?]
|
||||||
|
let withAndroidFineLocationArg = args[0] as! Bool
|
||||||
|
do {
|
||||||
|
let result = try api.hasPermissions(withAndroidFineLocation: withAndroidFineLocationArg)
|
||||||
|
reply(wrapResult(result))
|
||||||
|
} catch {
|
||||||
|
reply(wrapError(error))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
hasPermissionsChannel.setMessageHandler(nil)
|
||||||
|
}
|
||||||
let requestPermissionsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestPermissions\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
let requestPermissionsChannel = FlutterBasicMessageChannel(name: "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestPermissions\(channelSuffix)", binaryMessenger: binaryMessenger, codec: codec)
|
||||||
if let api = api {
|
if let api = api {
|
||||||
requestPermissionsChannel.setMessageHandler { message, reply in
|
requestPermissionsChannel.setMessageHandler { message, reply in
|
||||||
|
|||||||
@@ -56,6 +56,10 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func hasPermissions(withAndroidFineLocation _: Bool) throws -> Bool {
|
||||||
|
return CBCentralManager.authorization == .allowedAlways
|
||||||
|
}
|
||||||
|
|
||||||
func requestPermissions(withAndroidFineLocation _: Bool, completion: @escaping (Result<Void, any Error>) -> Void) {
|
func requestPermissions(withAndroidFineLocation _: Bool, completion: @escaping (Result<Void, any Error>) -> Void) {
|
||||||
if manager.state != .unknown {
|
if manager.state != .unknown {
|
||||||
completePermissionRequest(completion: completion)
|
completePermissionRequest(completion: completion)
|
||||||
@@ -114,21 +118,9 @@ private class BleCentralDarwin: NSObject, UniversalBlePlatformChannel, CBCentral
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isScanning() throws -> Bool {
|
func isScanning() throws -> Bool {
|
||||||
var hasAuthorization = true
|
if CBCentralManager.authorization == .allowedAlways {
|
||||||
#if os(iOS)
|
|
||||||
if #available(iOS 13.1, *) {
|
|
||||||
hasAuthorization = CBCentralManager.authorization == .allowedAlways
|
|
||||||
} else {
|
|
||||||
return isManageScanning
|
|
||||||
}
|
|
||||||
#elseif os(macOS)
|
|
||||||
hasAuthorization = CBCentralManager.authorization == .allowedAlways
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if hasAuthorization {
|
|
||||||
return manager.isScanning
|
return manager.isScanning
|
||||||
}
|
}
|
||||||
|
|
||||||
return isManageScanning
|
return isManageScanning
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ A new Flutter plugin project.
|
|||||||
s.source_files = 'Classes/**/*'
|
s.source_files = 'Classes/**/*'
|
||||||
s.ios.dependency 'Flutter'
|
s.ios.dependency 'Flutter'
|
||||||
s.osx.dependency 'FlutterMacOS'
|
s.osx.dependency 'FlutterMacOS'
|
||||||
s.ios.deployment_target = '9.0'
|
s.ios.deployment_target = '13.1'
|
||||||
s.osx.deployment_target = '10.12'
|
s.osx.deployment_target = '10.15'
|
||||||
|
|
||||||
# Flutter.framework does not contain a i386 slice.
|
# Flutter.framework does not contain a i386 slice.
|
||||||
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ EXTERNAL SOURCES:
|
|||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
|
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
|
||||||
universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
|
universal_ble: 65e1257dffc557cc7991a93d253beeddc7c1dc92
|
||||||
|
|
||||||
PODFILE CHECKSUM: 4f1c12611da7338d21589c0b2ecd6bd20b109694
|
PODFILE CHECKSUM: 4f1c12611da7338d21589c0b2ecd6bd20b109694
|
||||||
|
|
||||||
|
|||||||
@@ -216,7 +216,20 @@ class _MyAppState extends State<MyApp> {
|
|||||||
showSnackbar("BluetoothDisabled: $isDisabled");
|
showSnackbar("BluetoothDisabled: $isDisabled");
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
if (BleCapabilities.requiresRuntimePermission)
|
if (BleCapabilities.requiresRuntimePermission) ...[
|
||||||
|
PlatformButton(
|
||||||
|
text: 'Has Permissions',
|
||||||
|
onPressed: () async {
|
||||||
|
try {
|
||||||
|
bool hasPermissions = await UniversalBle.hasPermissions(
|
||||||
|
withAndroidFineLocation: false,
|
||||||
|
);
|
||||||
|
showSnackbar("Has Permissions: $hasPermissions");
|
||||||
|
} catch (e) {
|
||||||
|
showSnackbar(e.toString());
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
PlatformButton(
|
PlatformButton(
|
||||||
text: 'Request Permissions',
|
text: 'Request Permissions',
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@@ -230,6 +243,7 @@ class _MyAppState extends State<MyApp> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
],
|
||||||
if (!isTrackingAvailabilityState)
|
if (!isTrackingAvailabilityState)
|
||||||
PlatformButton(
|
PlatformButton(
|
||||||
text: 'Track Availability State',
|
text: 'Track Availability State',
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ EXTERNAL SOURCES:
|
|||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1
|
||||||
universal_ble: cf52a7b3fd2e7c14d6d7262e9fdadb72ab6b88a6
|
universal_ble: 65e1257dffc557cc7991a93d253beeddc7c1dc92
|
||||||
|
|
||||||
PODFILE CHECKSUM: 9ebaf0ce3d369aaa26a9ea0e159195ed94724cf3
|
PODFILE CHECKSUM: 9ebaf0ce3d369aaa26a9ea0e159195ed94724cf3
|
||||||
|
|
||||||
|
|||||||
@@ -195,7 +195,6 @@
|
|||||||
BF9EC7C1208428926C8C045E /* Pods-RunnerTests.release.xcconfig */,
|
BF9EC7C1208428926C8C045E /* Pods-RunnerTests.release.xcconfig */,
|
||||||
C5F6162E162B944885A4C57E /* Pods-RunnerTests.profile.xcconfig */,
|
C5F6162E162B944885A4C57E /* Pods-RunnerTests.profile.xcconfig */,
|
||||||
);
|
);
|
||||||
name = Pods;
|
|
||||||
path = Pods;
|
path = Pods;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
@@ -575,6 +574,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
@@ -701,6 +701,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
@@ -721,6 +722,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -214,10 +214,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.16.0"
|
version: "1.17.0"
|
||||||
path:
|
path:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -315,10 +315,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
|
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.6"
|
version: "0.7.7"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -71,6 +71,18 @@ class UniversalBle {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if has permissions.
|
||||||
|
/// [withAndroidFineLocation] is used to check fine location permission on Android 12+ (API 31+).
|
||||||
|
/// On Android lower than 12, this method will check location permission regardless of the [withAndroidFineLocation] value.
|
||||||
|
/// `Windows`, `Linux` and `Web` will always return true.
|
||||||
|
static Future<bool> hasPermissions({
|
||||||
|
bool withAndroidFineLocation = false,
|
||||||
|
}) async {
|
||||||
|
return _platform.hasPermissions(
|
||||||
|
withAndroidFineLocation: withAndroidFineLocation,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Request permissions.
|
/// Request permissions.
|
||||||
/// if all permissions are already granted or granted by user, this method will succeed.
|
/// if all permissions are already granted or granted by user, this method will succeed.
|
||||||
/// it will throw exception if permissions are denied by user.
|
/// it will throw exception if permissions are denied by user.
|
||||||
|
|||||||
@@ -431,12 +431,6 @@ class UniversalBleLinux extends UniversalBlePlatform {
|
|||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> requestPermissions(
|
|
||||||
{bool withAndroidFineLocation = false}) async {
|
|
||||||
// No permissions to request on linux
|
|
||||||
}
|
|
||||||
|
|
||||||
AvailabilityState get _availabilityState {
|
AvailabilityState get _availabilityState {
|
||||||
return _activeAdapter?.powered == true
|
return _activeAdapter?.powered == true
|
||||||
? AvailabilityState.poweredOn
|
? AvailabilityState.poweredOn
|
||||||
|
|||||||
@@ -592,6 +592,35 @@ class UniversalBlePlatformChannel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> hasPermissions(bool withAndroidFineLocation) async {
|
||||||
|
final pigeonVar_channelName =
|
||||||
|
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.hasPermissions$pigeonVar_messageChannelSuffix';
|
||||||
|
final pigeonVar_channel = BasicMessageChannel<Object?>(
|
||||||
|
pigeonVar_channelName,
|
||||||
|
pigeonChannelCodec,
|
||||||
|
binaryMessenger: pigeonVar_binaryMessenger,
|
||||||
|
);
|
||||||
|
final Future<Object?> pigeonVar_sendFuture =
|
||||||
|
pigeonVar_channel.send(<Object?>[withAndroidFineLocation]);
|
||||||
|
final pigeonVar_replyList = await pigeonVar_sendFuture as List<Object?>?;
|
||||||
|
if (pigeonVar_replyList == null) {
|
||||||
|
throw _createConnectionError(pigeonVar_channelName);
|
||||||
|
} else if (pigeonVar_replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: pigeonVar_replyList[0]! as String,
|
||||||
|
message: pigeonVar_replyList[1] as String?,
|
||||||
|
details: pigeonVar_replyList[2],
|
||||||
|
);
|
||||||
|
} else if (pigeonVar_replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (pigeonVar_replyList[0] as bool?)!;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> requestPermissions(bool withAndroidFineLocation) async {
|
Future<void> requestPermissions(bool withAndroidFineLocation) async {
|
||||||
final pigeonVar_channelName =
|
final pigeonVar_channelName =
|
||||||
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestPermissions$pigeonVar_messageChannelSuffix';
|
'dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestPermissions$pigeonVar_messageChannelSuffix';
|
||||||
|
|||||||
@@ -145,6 +145,13 @@ class UniversalBlePigeonChannel extends UniversalBlePlatform {
|
|||||||
Future<void> unpair(String deviceId) =>
|
Future<void> unpair(String deviceId) =>
|
||||||
_executeWithErrorHandling(() => _channel.unPair(deviceId));
|
_executeWithErrorHandling(() => _channel.unPair(deviceId));
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> hasPermissions({bool withAndroidFineLocation = false}) async {
|
||||||
|
return await _executeWithErrorHandling(
|
||||||
|
() => _channel.hasPermissions(withAndroidFineLocation),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> requestPermissions(
|
Future<void> requestPermissions(
|
||||||
{bool withAndroidFineLocation = false}) async {
|
{bool withAndroidFineLocation = false}) async {
|
||||||
|
|||||||
@@ -37,7 +37,12 @@ abstract class UniversalBlePlatform {
|
|||||||
|
|
||||||
Future<bool> disableBluetooth();
|
Future<bool> disableBluetooth();
|
||||||
|
|
||||||
Future<void> requestPermissions({bool withAndroidFineLocation = false});
|
Future<bool> hasPermissions({bool withAndroidFineLocation = false}) async {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> requestPermissions(
|
||||||
|
{bool withAndroidFineLocation = false}) async {}
|
||||||
|
|
||||||
Future<void> startScan({
|
Future<void> startScan({
|
||||||
ScanFilter? scanFilter,
|
ScanFilter? scanFilter,
|
||||||
|
|||||||
@@ -275,12 +275,6 @@ class UniversalBleWeb extends UniversalBlePlatform {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// `Unimplemented`
|
/// `Unimplemented`
|
||||||
@override
|
|
||||||
Future<void> requestPermissions(
|
|
||||||
{bool withAndroidFineLocation = false}) async {
|
|
||||||
// No permissions to request on Web
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<int> requestMtu(String deviceId, int expectedMtu) {
|
Future<int> requestMtu(String deviceId, int expectedMtu) {
|
||||||
throw UniversalBleException(
|
throw UniversalBleException(
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ abstract class UniversalBlePlatformChannel {
|
|||||||
@async
|
@async
|
||||||
int getBluetoothAvailabilityState();
|
int getBluetoothAvailabilityState();
|
||||||
|
|
||||||
|
bool hasPermissions(bool withAndroidFineLocation);
|
||||||
|
|
||||||
@async
|
@async
|
||||||
void requestPermissions(bool withAndroidFineLocation);
|
void requestPermissions(bool withAndroidFineLocation);
|
||||||
|
|
||||||
|
|||||||
@@ -612,6 +612,34 @@ void UniversalBlePlatformChannel::SetUp(
|
|||||||
channel.SetMessageHandler(nullptr);
|
channel.SetMessageHandler(nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.hasPermissions" + prepended_suffix, &GetCodec());
|
||||||
|
if (api != nullptr) {
|
||||||
|
channel.SetMessageHandler([api](const EncodableValue& message, const flutter::MessageReply<EncodableValue>& reply) {
|
||||||
|
try {
|
||||||
|
const auto& args = std::get<EncodableList>(message);
|
||||||
|
const auto& encodable_with_android_fine_location_arg = args.at(0);
|
||||||
|
if (encodable_with_android_fine_location_arg.IsNull()) {
|
||||||
|
reply(WrapError("with_android_fine_location_arg unexpectedly null."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto& with_android_fine_location_arg = std::get<bool>(encodable_with_android_fine_location_arg);
|
||||||
|
ErrorOr<bool> output = api->HasPermissions(with_android_fine_location_arg);
|
||||||
|
if (output.has_error()) {
|
||||||
|
reply(WrapError(output.error()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
EncodableList wrapped;
|
||||||
|
wrapped.push_back(EncodableValue(std::move(output).TakeValue()));
|
||||||
|
reply(EncodableValue(std::move(wrapped)));
|
||||||
|
} catch (const std::exception& exception) {
|
||||||
|
reply(WrapError(exception.what()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.SetMessageHandler(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
{
|
{
|
||||||
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestPermissions" + prepended_suffix, &GetCodec());
|
BasicMessageChannel<> channel(binary_messenger, "dev.flutter.pigeon.universal_ble.UniversalBlePlatformChannel.requestPermissions" + prepended_suffix, &GetCodec());
|
||||||
if (api != nullptr) {
|
if (api != nullptr) {
|
||||||
|
|||||||
@@ -387,6 +387,7 @@ class UniversalBlePlatformChannel {
|
|||||||
UniversalBlePlatformChannel& operator=(const UniversalBlePlatformChannel&) = delete;
|
UniversalBlePlatformChannel& operator=(const UniversalBlePlatformChannel&) = delete;
|
||||||
virtual ~UniversalBlePlatformChannel() {}
|
virtual ~UniversalBlePlatformChannel() {}
|
||||||
virtual void GetBluetoothAvailabilityState(std::function<void(ErrorOr<int64_t> reply)> result) = 0;
|
virtual void GetBluetoothAvailabilityState(std::function<void(ErrorOr<int64_t> reply)> result) = 0;
|
||||||
|
virtual ErrorOr<bool> HasPermissions(bool with_android_fine_location) = 0;
|
||||||
virtual void RequestPermissions(
|
virtual void RequestPermissions(
|
||||||
bool with_android_fine_location,
|
bool with_android_fine_location,
|
||||||
std::function<void(std::optional<FlutterError> reply)> result) = 0;
|
std::function<void(std::optional<FlutterError> reply)> result) = 0;
|
||||||
|
|||||||
@@ -119,6 +119,11 @@ void UniversalBlePlugin::DisableBluetooth(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ErrorOr<bool> UniversalBlePlugin::HasPermissions(bool with_android_fine_location) {
|
||||||
|
// Windows does not require runtime permissions for Bluetooth
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void UniversalBlePlugin::RequestPermissions(
|
void UniversalBlePlugin::RequestPermissions(
|
||||||
bool with_android_fine_location,
|
bool with_android_fine_location,
|
||||||
std::function<void(std::optional<FlutterError> reply)> result) {
|
std::function<void(std::optional<FlutterError> reply)> result) {
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ private:
|
|||||||
ErrorOr<bool> IsScanning() override;
|
ErrorOr<bool> IsScanning() override;
|
||||||
std::optional<FlutterError> Connect(const std::string &device_id) override;
|
std::optional<FlutterError> Connect(const std::string &device_id) override;
|
||||||
std::optional<FlutterError> Disconnect(const std::string &device_id) override;
|
std::optional<FlutterError> Disconnect(const std::string &device_id) override;
|
||||||
|
ErrorOr<bool> HasPermissions(bool with_android_fine_location) override;
|
||||||
void RequestPermissions(
|
void RequestPermissions(
|
||||||
bool with_android_fine_location,
|
bool with_android_fine_location,
|
||||||
std::function<void(std::optional<FlutterError> reply)> result) override;
|
std::function<void(std::optional<FlutterError> reply)> result) override;
|
||||||
|
|||||||
Reference in New Issue
Block a user