Last active
April 17, 2024 00:00
-
-
Save salarcode/da8ad2b993e67c602db88a62259d0456 to your computer and use it in GitHub Desktop.
MAUI Bluetooth Permission Declaration (for Android)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// IMPORTANT: Put this file in `Platforms/Android/` directory. | |
using Microsoft.Maui.ApplicationModel; | |
using System.Collections.Generic; | |
/// <summary> | |
/// MAUI Bluetooth Permission Decleration (for Android). | |
/// </summary> | |
/// <remarks> | |
/// IMPORTANT: Put this file in `Platforms/Android/` directory. | |
/// </remarks> | |
public partial class BluetoothPermissions : Permissions.BasePlatformPermission | |
{ | |
private readonly bool _scan; | |
private readonly bool _advertise; | |
private readonly bool _connect; | |
private readonly bool _bluetoothLocation; | |
public BluetoothPermissions() | |
: this(true, false, true, false) | |
{ | |
} | |
/// <summary> | |
/// | |
/// </summary> | |
/// <param name="scan">Needed only if your app looks for Bluetooth devices. | |
/// If your app doesn't use Bluetooth scan results to derive physical location, you can make a strong assertion that your app never uses the Bluetooth permissions to derive physical location. | |
/// Add the `android:usesPermissionFlags` attribute to your BLUETOOTH_SCAN permission declaration, and set this attribute's value to `neverForLocation`. | |
/// </param> | |
/// <param name="advertise">Needed only if your app makes the device discoverable to Bluetooth devices.</param> | |
/// <param name="connect">Needed only if your app communicates with already-paired Bluetooth devices.</param> | |
/// <param name="bluetoothLocation">Needed only if your app uses Bluetooth scan results to derive physical location.</param> | |
/// <remarks> | |
/// https://developer.android.com/guide/topics/connectivity/bluetooth/permissions | |
/// </remarks> | |
public BluetoothPermissions(bool scan = true, bool advertise = true, bool connect = true, bool bluetoothLocation = true) | |
{ | |
_scan = scan; | |
_advertise = advertise; | |
_connect = connect; | |
_bluetoothLocation = bluetoothLocation; | |
} | |
private (string androidPermission, bool isRuntime)[] _requiredPermissions; | |
public override (string androidPermission, bool isRuntime)[] RequiredPermissions | |
{ | |
get | |
{ | |
if (_requiredPermissions != null) | |
return _requiredPermissions; | |
var result = new List<(string androidPermission, bool isRuntime)>(); | |
var sdk = (int)Android.OS.Build.VERSION.SdkInt; | |
if (sdk >= 31) | |
{ | |
// If your app targets Android 12 (API level 31) or higher, declare the following permissions in your app's manifest file: | |
if (_scan) | |
result.Add((global::Android.Manifest.Permission.BluetoothScan, true)); | |
if (_advertise) | |
result.Add((global::Android.Manifest.Permission.BluetoothAdvertise, true)); | |
if (_connect) | |
result.Add((global::Android.Manifest.Permission.BluetoothConnect, true)); | |
if (_bluetoothLocation) | |
result.Add((global::Android.Manifest.Permission.AccessFineLocation, true)); | |
} | |
else | |
{ | |
// If your app targets Android 11 (API level 30) or lower, declare the following permissions in your app's manifest file: | |
result.Add((global::Android.Manifest.Permission.Bluetooth, true)); | |
if (sdk >= 29) | |
{ | |
result.Add((global::Android.Manifest.Permission.AccessFineLocation, true)); | |
} | |
else | |
{ | |
// If your app targets Android 9 (API level 28) or lower, you can declare the ACCESS_COARSE_LOCATION permission instead of the ACCESS_FINE_LOCATION permission. | |
result.Add((global::Android.Manifest.Permission.AccessCoarseLocation, true)); | |
} | |
if (_scan || _connect || _advertise) | |
{ | |
result.Add((global::Android.Manifest.Permission.BluetoothAdmin, true)); | |
if (sdk >= 29) | |
{ | |
// If your app supports a service and can run on Android 10 (API level 29) or Android 11, you must also declare the ACCESS_BACKGROUND_LOCATION permission to discover Bluetooth devices. | |
result.Add((global::Android.Manifest.Permission.AccessBackgroundLocation, true)); | |
} | |
} | |
} | |
_requiredPermissions = result.ToArray(); | |
return _requiredPermissions; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <summary> | |
/// IMPORTANT: Put this file in root of Application | |
/// </summary> | |
public partial class BluetoothPermissions : Permissions.BasePlatformPermission | |
{ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// How to use MAUI Bluetooth LE permissions | |
private async Task<bool> CheckBluetoothStatus() | |
{ | |
try | |
{ | |
var requestStatus = await new BluetoothPermissions().CheckStatusAsync(); | |
return requestStatus == PermissionStatus.Granted; | |
} | |
catch (Exception ex) | |
{ | |
// logger.LogError(ex); | |
return false; | |
} | |
} | |
public async Task<bool> RequestBluetoothAccess() | |
{ | |
try | |
{ | |
var requestStatus = await new BluetoothPermissions().RequestAsync(); | |
return requestStatus == PermissionStatus.Granted; | |
} | |
catch (Exception ex) | |
{ | |
// logger.LogError(ex); | |
return false; | |
} | |
} |
Shoutout to @salarcode , this was of amazing help saved many hours of banging my head against a wall
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Correct me if im wrong but, it appears to me neither
Bluetooth
norBluetoothAdmin
are runtime permissions, and thus do not need the runtime parameter to be true.From what i see in the source code, the only use to even include them in this
RequiredPermissions
override is to let MAUI ensure they are part of the manifest, but marking them as runtime is misleading/confusing.Also it seems to me that since CheckStatus/Request permissions only returns a single over all
PermissionStatus
, than adding both the Bluetooth specific as well as Location specific permissions, to the sameRequiredPermissions
enumeration, will not allow you to differentiate which permissions if any had not been granted, rather just that something wasn't.So if you need to know which may have been denied, you wont unless they are requested separately.
For after the permission is denied twice, additional request will not be granted, and arguably you might want to know what exactly was denied to help guide the user to allow the permission manually.