英文:
An easy way to check and request Maui permissions, including Bluetooth?
问题
I'm looking for an easy way to query for and ask for multiple permissions in .Net Maui.
我正在寻找一种简单的方法来查询和请求多个权限在 .Net Maui 中。
I also need to request Bluetooth permissions, but Maui doesn't have that built-in, so far as I can see.
我还需要请求蓝牙权限,但据我所见,Maui没有内置这个功能。
The last I heard from 2022 was that Bluetooth wasn't in the Maui roadmap, which I find to be pretty shortsighted.
我上次听说的是,从2022年起,蓝牙不在Maui的路线图中,我觉得这相当短视。
I've read a bunch of articles, Q&A's, documentation, watched videos, and done lots of other research to figure this out.
我已经阅读了大量的文章、问答、文档,观看了视频,以及进行了许多其他研究来弄清楚这个问题。
So far, I can check and request individual permissions, but I'm looking for something more DRY than repeating:
到目前为止,我可以检查和请求单个权限,但我正在寻找比重复更加干净的方法:
for every permission. I need several permissions, so my checks are quickly getting out of hand.
对于每个权限。我需要多个权限,所以我的检查迅速变得混乱。
BTW, I'm targeting Android 13.0, as per Google Play requirements.
顺便说一下,我以Google Play的要求为目标,目标是Android 13.0。
I know this is asking for a lot, but I'm getting lost in all the suggestions, many of which don't work, so help sorting out all this would be great!
我知道这要求很多,但我在所有的建议中迷失了方向,其中许多都不起作用,所以帮助整理这一切将会很好!
英文:
I'm looking for an easy way to query for and ask for multiple permissions in .Net Maui.
I also need to request Bluetooth permissions, but Maui doesn't have that built-in, so far as I can see. The last I heard from 2022 was that Bluetooth wasn't in the Maui roadmap, which I find to be pretty shortsighted.
I've read a bunch of articles, Q&A's, documentation, watched videos, and done lots of other research to figure this out.
So far, I can check and request individual permissions, but I'm looking for something more DRY than repeating:
PermissionStatus status = await Permissions.CheckStatusAsync<Permissions.XXX>();
if (status != PermissionStatus.Granted){
status = await Permissions.RequestAsync<Permissions.XXX>();
}
for every permission. I need several permissions, so my checks are quickly getting out of hand.
BTW, I'm targeting Android 13.0, as per Google Play requirements.
I know this is asking for a lot, but I'm getting lost in all the suggestions, many of which don't work, so help sorting out all this would be great!
答案1
得分: 2
以下是翻译好的内容:
我使用了许多不同的来源来整理这些信息,但这些是最有用的。
可用的 Maui 权限:https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/appmodel/permissions
主线程上的权限:https://stackoverflow.com/a/75574570/1836461
蓝牙和自定义权限:https://www.youtube.com/watch?v=9GljgwfpiiE
位置服务检查:https://stackoverflow.com/a/69158717/1836461
为了让这更容易理解,我将所有的代码都留到最后。
此外,我的项目针对 .Net 7.0,当前的 Google Play 要求是 Android 13.0(API 33),iOS 11.0 和 Windows 10.0.19041.0。我还没有检查这是否适用于 iOS 或 Windows,但至少可以帮助您/我朝着运行这些其他操作系统迈出了几步。在更改 JIT 编译器的目标操作系统时,VS 2022 不会引发任何错误,所以它应该可以正常工作。如果不行,那么需要进行的调整应该比1-5年前的建议或专门为Android编写的Java代码要少。
第一步
您需要设置清单和 .plist 文件以获取您需要的正确权限。我不会在这里详细介绍,因为在我上面链接的第一个参考资料中已经很好地解释了这一点。
第二步
我建议将您的权限检查代码放入一个辅助方法中。由于这将是一个async
方法,您需要从“OnAppearing”方法中调用它,您必须重写该方法并使其成为async
方法。
我让我的辅助方法返回一个bool
,这样我就可以检查是否已接受了所有权限,因为我的应用程序需要我请求的所有权限。如果没有这些权限,它根本不会执行任何操作。为了轻松检查权限是否已授予/受限,我添加了另一个方法,因为我要检查许多权限。
第三步
您可以将单独的CheckStatusAsync
和RequestAsync
移动到一个通用方法中,并简单地调用该方法以防止重复。
第四步
由于您/我需要蓝牙访问权限,所以您需要编写一个自定义的权限检查器,但只适用于Android,而不适用于iOS或Windows。这并不难,但没有多少资源可以告诉您如何做,而且也不容易找到。
第五步
显然,如果手机上的位置服务没有启用,那么所有这些对蓝牙权限的检查都无关紧要。如果您的应用程序可以扫描蓝牙设备,但没有发现任何设备,那么手机的位置服务可能没有启用。我不得不添加一个提示,告诉用户扫描返回零结果时需要启用位置服务。
或者您可以直接使用特定于操作系统的方法来检查位置服务状态。
代码
MainPage.xaml.cs:
using CommunityToolkit.Maui.Alerts; // 用于 Toast
#if ANDROID
using Android.Content;
using Android.Locations;
#elif IOS || MACCATALYST
using CoreLocation;
#elif WINDOWS
using Windows.Devices.Geolocation;
#endif
protected override async void OnAppearing()
{
base.OnAppearing();
if (!await CheckPermissions())
{
await Toast.Make("未接受所有权限。应用程序将关闭。").Show();
Application.Current.Quit();
}
}
private async Task<bool> CheckPermissions()
{
PermissionStatus bluetoothStatus = await CheckBluetoothPermissions();
PermissionStatus cameraStatus = await CheckPermissions<Permissions.Camera>();
PermissionStatus mediaStatus = await CheckPermissions<Permissions.Media>();
PermissionStatus storageWriteStatus = await CheckPermissions<Permissions.StorageWrite>();
//PermissionStatus photosStatus = await CheckPermissions<Permissions.Photos>();
...
bool locationServices = IsLocationServiceEnabled();
return IsGranted(cameraStatus) && IsGranted(mediaStatus) && IsGranted(storageWriteStatus) && IsGranted(bluetoothStatus);
}
private async Task<PermissionStatus> CheckBluetoothPermissions()
{
PermissionStatus bluetoothStatus = PermissionStatus.Granted;
if (DeviceInfo.Platform == DevicePlatform.Android)
{
if (DeviceInfo.Version.Major >= 12)
{
bluetoothStatus = await CheckPermissions<BluetoothPermissions>();
}
else
{
bluetoothStatus = await CheckPermissions<Permissions.LocationWhenInUse>();
}
}
return bluetoothStatus;
}
private async Task<PermissionStatus> CheckPermissions<TPermission>() where TPermission : Permissions.BasePermission, new()
{
PermissionStatus status = await Permissions.CheckStatusAsync<TPermission>();
if (status != PermissionStatus.Granted){
status = await Permissions.RequestAsync<TPermission>();
}
return status;
}
private static bool IsGranted(PermissionStatus status)
{
return status == PermissionStatus.Granted || status == PermissionStatus.Limited;
}
#if ANDROID
private bool IsLocationServiceEnabled()
{
LocationManager locationManager = (LocationManager)Android.App.Application.Context.GetSystemService(Context.LocationService);
return locationManager.IsProviderEnabled(LocationManager.GpsProvider);
}
#elif IOS || MACCATALYST
public bool IsLocationServiceEnabled()
{
return CLLocationManager.Status == CLAuthorizationStatus.Denied;
}
#elif WINDOWS
private bool IsLocationServiceEnabled()
{
Geolocator locationservice = new Geolocator();
return locationservice.LocationStatus == PositionStatus.Disabled;
}
#endif
在项目中创建一个名为 "BluetoothPermissions.cs" 的新文件:
using static Microsoft.Maui.ApplicationModel.Permissions;
namespace YourNamespace
{
internal class BluetoothPermissions : BasePlatformPermission
{
#if ANDROID
public override (string androidPermission, bool isRuntime)[] RequiredPermissions =>
new List<(string permission, bool isRuntime)>
{
("android.permission.BLUETOOTH", true),
("android.permission.BLUETOOTH_ADMIN", true),
("android.permission.BLUETOOTH_SCAN", true),
("android.permission.BLUETOOTH_CONNECT", true),
("android.permission.ACCESS_COARSE_LOCATION", true),
("android.permission.ACCESS_FINE_LOCATION", true)
}.ToArray();
// 此列表包括 Bluetooth LE 权限。
// 您需要在 Android Manifest 中包含这些权限。
#endif
}
<details>
<summary>英文:</summary>
I've used many different sources to put this together, but these were the most useful.
Available Maui Permissions: https://learn.microsoft.com/en-us/dotnet/maui/platform-integration/appmodel/permissions
Permission on main thread: https://stackoverflow.com/a/75574570/1836461
Bluetooth and custom permissions: https://www.youtube.com/watch?v=9GljgwfpiiE
Location Services check: https://stackoverflow.com/a/69158717/1836461
To make this easier to follow, I'm going to leave all the code for last.
Also, my project is targeting .Net 7.0, the current Google Play requirements of Android 13.0 (API 33), iOS 11.0, and Windows 10.0.19041.0. I haven't checked if this works for iOS or Windows, but this at least gets you/me several steps towards running these other OSes. VS 2022 doesn't throw any error when changing the target OS for the JIT compiler, so it should work. If it doesn't, there should be fewer adjustment to do than suggestions from 1-5+ years ago or those written in Java specifically for Android.
First
-
You'll need to set up your Manifest and .plist files for the correct permissions for your needs. I'm not going to go through that here, since it's covered nicely in the first reference I've linked above.
Second
-
I suggest putting your permission checking code into a helper method. Since this is going to be an `async` method, you'll need to do call it from the "OnAppearing" method, which you'll have to override and make `async`.
I made my helper method return a `bool`, so I could check if all the permissions were accepted, since my app requires all the permissions I ask for. Without them, it simply wouldn't do anything. To easily check if the permissions were granted/limited, I added another method for that, since I'm checking so many permissions.
Third
-
You can move the individual `CheckStatusAsync` and `RequestAsync` to a generic method and simply call that to help prevent repetition.
Fourth
-
Since you/I need Bluetooth access, you'll have to write a custom permissions checker, but only for Android and not iOS or Windows. It's not hard, but there aren't many resources to show you how and they aren't easy to find, either.
Fifth
-
Apparently, none of this matters for Bluetooth permissions if the Location Services on the phone aren't enabled. If your app can scan for Bluetooth devices, but none are found, it's a good bet the phone's Location Services aren't enabled. I've had to add a Toast to let the user know it's necessary when the scans return zero results.
Or you can check the Location Services status directly using OS specific methods.
Code
-
MainPage.xaml.cs:
using CommunityToolkit.Maui.Alerts; // For the Toast
#if ANDROID
using Android.Content;
using Android.Locations;
#elif IOS || MACCATALYST
using CoreLocation;
#elif WINDOWS
using Windows.Devices.Geolocation;
#endif
protected override async void OnAppearing()
{
base.OnAppearing();
if (!await CheckPermissions())
{
await Toast.Make("Not all permissions were accepted. Application will close.").Show();
Application.Current.Quit();
}
}
private async Task<bool> CheckPermissions()
{
PermissionStatus bluetoothStatus = await CheckBluetoothPermissions();
PermissionStatus cameraStatus = await CheckPermissions<Permissions.Camera>();
PermissionStatus mediaStatus = await CheckPermissions<Permissions.Media>();
PermissionStatus storageWriteStatus = await CheckPermissions<Permissions.StorageWrite>();
//PermissionStatus photosStatus = await CheckPermissions<Permissions.Photos>();
...
bool locationServices = IsLocationServiceEnabled();
return IsGranted(cameraStatus) && IsGranted(mediaStatus) && IsGranted(storageWriteStatus) && IsGranted(bluetoothStatus);
}
private async Task<PermissionStatus> CheckBluetoothPermissions()
{
PermissionStatus bluetoothStatus = PermissionStatus.Granted;
if (DeviceInfo.Platform == DevicePlatform.Android)
{
if (DeviceInfo.Version.Major >= 12)
{
bluetoothStatus = await CheckPermissions<BluetoothPermissions>();
}
else
{
bluetoothStatus = await CheckPermissions<Permissions.LocationWhenInUse>();
}
}
return bluetoothStatus;
}
private async Task<PermissionStatus> CheckPermissions<TPermission>() where TPermission : Permissions.BasePermission, new()
{
PermissionStatus status = await Permissions.CheckStatusAsync<TPermission>();
if (status != PermissionStatus.Granted){
status = await Permissions.RequestAsync<TPermission>();
}
return status;
}
private static bool IsGranted(PermissionStatus status)
{
return status == PermissionStatus.Granted || status == PermissionStatus.Limited;
}
#if ANDROID
private bool IsLocationServiceEnabled()
{
LocationManager locationManager = (LocationManager)Android.App.Application.Context.GetSystemService(Context.LocationService);
return locationManager.IsProviderEnabled(LocationManager.GpsProvider);
}
#elif IOS || MACCATALYST
public bool IsLocationServiceEnabled()
{
return CLLocationManager.Status == CLAuthorizationStatus.Denied;
}
#elif WINDOWS
private bool IsLocationServiceEnabled()
{
Geolocator locationservice = new Geolocator();
return locationservice.LocationStatus == PositionStatus.Disabled;
}
#endif
Create a new file in your project called "BluetoothPermissions.cs":
using static Microsoft.Maui.ApplicationModel.Permissions;
namespace YourNamespace;
internal class BluetoothPermissions : BasePlatformPermission
{
#if ANDROID
public override (string androidPermission, bool isRuntime)[] RequiredPermissions =>
new List<(string permission, bool isRuntime)>
{
("android.permission.BLUETOOTH", true),
("android.permission.BLUETOOTH_ADMIN", true),
("android.permission.BLUETOOTH_SCAN", true),
("android.permission.BLUETOOTH_CONNECT", true),
("android.permission.ACCESS_COARSE_LOCATION", true),
("android.permission.ACCESS_FINE_LOCATION", true)
}.ToArray();
// This list includes Bluetooth LE permissions.
// You will need to include these permissions in your Android Manifest, too.
#endif
}
**Notes:** As I continue working on this app, I'm finding ways to use features without all the permissions needing to be required, like Bluetooth. As I make these features able to be turned on and off, I'm finding it increasingly likely that I will have to check for these permissions on settings pages other than the "MainPage". I'm probably going to end up turning most (if not all) of these permissions into it's own class so I can access it from the various places I'll be turning features on and off. If I do that, I'll leave this answer as is and make a new answer, if it's significantly different than this one.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论