From f1d8177499fc458ca36c8b9abaa03bddedf32b67 Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Mon, 6 Dec 2021 21:45:36 -0500 Subject: [PATCH 1/3] Fix #1912 - if coarse is provided instead of fine location, returned restricted, not denied --- .../Permissions/Permissions.android.cs | 111 +++++++++++------- 1 file changed, 71 insertions(+), 40 deletions(-) diff --git a/Xamarin.Essentials/Permissions/Permissions.android.cs b/Xamarin.Essentials/Permissions/Permissions.android.cs index f8afa8f69..3fb5029cb 100644 --- a/Xamarin.Essentials/Permissions/Permissions.android.cs +++ b/Xamarin.Essentials/Permissions/Permissions.android.cs @@ -29,10 +29,23 @@ public static bool IsDeclaredInManifest(string permission) internal static void OnRequestPermissionsResult(int requestCode, string[] permissions, Permission[] grantResults) => BasePlatformPermission.OnRequestPermissionsResult(requestCode, permissions, grantResults); + public partial class PermissionResult + { + public PermissionResult(string[] permissions, Permission[] grantResults) + { + Permissions = permissions; + GrantResults = grantResults; + } + + public string[] Permissions { get; } + + public Permission[] GrantResults { get; } + } + public abstract partial class BasePlatformPermission : BasePermission { - static readonly Dictionary tcs)> requests = - new Dictionary)>(); + static readonly Dictionary> requests = + new Dictionary>(); static readonly object locker = new object(); static int requestCode; @@ -79,9 +92,6 @@ public override async Task RequestAsync() if (await CheckStatusAsync() == PermissionStatus.Granted) return PermissionStatus.Granted; - TaskCompletionSource tcs; - var doRequest = true; - var runtimePermissions = RequiredPermissions.Where(p => p.isRuntime) ?.Select(p => p.androidPermission)?.ToArray(); @@ -90,38 +100,32 @@ public override async Task RequestAsync() if (runtimePermissions == null || !runtimePermissions.Any()) return PermissionStatus.Granted; - var permissionId = string.Join(';', runtimePermissions); + var permissionResult = await DoRequest(runtimePermissions); + if (permissionResult.GrantResults.Any(g => g == Permission.Denied)) + return PermissionStatus.Denied; + + return PermissionStatus.Granted; + } + + protected virtual async Task DoRequest(string[] permissions) + { + TaskCompletionSource tcs; lock (locker) { - if (requests.ContainsKey(permissionId)) - { - tcs = requests[permissionId].tcs; - doRequest = false; - } - else - { - tcs = new TaskCompletionSource(); + tcs = new TaskCompletionSource(); - requestCode = Platform.NextRequestCode(); + requestCode = Platform.NextRequestCode(); - requests.Add(permissionId, (requestCode, tcs)); - } + requests.Add(requestCode, tcs); } - if (!doRequest) - return await tcs.Task; - if (!MainThread.IsMainThread) throw new PermissionException("Permission request must be invoked on main thread."); - ActivityCompat.RequestPermissions(Platform.GetCurrentActivity(true), runtimePermissions.ToArray(), requestCode); + ActivityCompat.RequestPermissions(Platform.GetCurrentActivity(true), permissions.ToArray(), requestCode); var result = await tcs.Task; - - if (requests.ContainsKey(permissionId)) - requests.Remove(permissionId); - return result; } @@ -157,22 +161,11 @@ internal static void OnRequestPermissionsResult(int requestCode, string[] permis { lock (locker) { - // Check our pending requests for one with a matching request code - foreach (var kvp in requests) + if (requests.ContainsKey(requestCode)) { - if (kvp.Value.requestCode == requestCode) - { - var tcs = kvp.Value.tcs; - - // Look for any denied requests, and deny the whole request if so - // Remember, each PermissionType is tied to 1 or more android permissions - // so if any android permissions denied the whole PermissionType is considered denied - if (grantResults.Any(g => g == Permission.Denied)) - tcs.TrySetResult(PermissionStatus.Denied); - else - tcs.TrySetResult(PermissionStatus.Granted); - break; - } + var result = new PermissionResult(permissions, grantResults); + requests[requestCode].TrySetResult(result); + requests.Remove(requestCode); } } } @@ -239,6 +232,24 @@ public override (string androidPermission, bool isRuntime)[] RequiredPermissions (Manifest.Permission.AccessCoarseLocation, true), (Manifest.Permission.AccessFineLocation, true) }; + + public override async Task RequestAsync() + { + // Check status before requesting first + if (await CheckStatusAsync() == PermissionStatus.Granted) + return PermissionStatus.Granted; + + var permissionResult = await DoRequest(new string[] { Manifest.Permission.AccessCoarseLocation, Manifest.Permission.AccessFineLocation }); + + // when requesting fine location, user can decline and set coarse instead + var count = permissionResult.GrantResults.Count(x => x == Permission.Granted); + return count switch + { + 2 => PermissionStatus.Granted, + 1 => PermissionStatus.Restricted, + _ => PermissionStatus.Denied + }; + } } public partial class LocationAlways : BasePlatformPermission @@ -260,6 +271,26 @@ public override (string androidPermission, bool isRuntime)[] RequiredPermissions return permissions.ToArray(); } } + + + public override async Task RequestAsync() + { + // Check status before requesting first + if (await CheckStatusAsync() == PermissionStatus.Granted) + return PermissionStatus.Granted; + + if (Platform.HasApiLevel(BuildVersionCodes.R)) + var permissionResult = await DoRequest(new string[] { Manifest.Permission.AccessCoarseLocation, Manifest.Permission.AccessFineLocation }); + + // when requesting fine location, user can decline and set coarse instead + var count = permissionResult.GrantResults.Count(x => x == Permission.Granted); + var result = count switch + { + 2 => PermissionStatus.Granted, + 1 => PermissionStatus.Restricted, + _ => PermissionStatus.Denied + }; + } } public partial class Maps : BasePlatformPermission From 81832e532ba0dacd1a5e2b926836e3186a22c1b6 Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Mon, 6 Dec 2021 21:50:56 -0500 Subject: [PATCH 2/3] Should not have been added 1912 --- .../Permissions/Permissions.android.cs | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/Xamarin.Essentials/Permissions/Permissions.android.cs b/Xamarin.Essentials/Permissions/Permissions.android.cs index 3fb5029cb..29793f80b 100644 --- a/Xamarin.Essentials/Permissions/Permissions.android.cs +++ b/Xamarin.Essentials/Permissions/Permissions.android.cs @@ -271,26 +271,6 @@ public override (string androidPermission, bool isRuntime)[] RequiredPermissions return permissions.ToArray(); } } - - - public override async Task RequestAsync() - { - // Check status before requesting first - if (await CheckStatusAsync() == PermissionStatus.Granted) - return PermissionStatus.Granted; - - if (Platform.HasApiLevel(BuildVersionCodes.R)) - var permissionResult = await DoRequest(new string[] { Manifest.Permission.AccessCoarseLocation, Manifest.Permission.AccessFineLocation }); - - // when requesting fine location, user can decline and set coarse instead - var count = permissionResult.GrantResults.Count(x => x == Permission.Granted); - var result = count switch - { - 2 => PermissionStatus.Granted, - 1 => PermissionStatus.Restricted, - _ => PermissionStatus.Denied - }; - } } public partial class Maps : BasePlatformPermission From 414ce94041501eb438c6091d7205b7f2306f0438 Mon Sep 17 00:00:00 2001 From: Allan Ritchie Date: Mon, 6 Dec 2021 22:32:11 -0500 Subject: [PATCH 3/3] Fix #1936 - move bg permission to separate request on api30+ --- .../Permissions/Permissions.android.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/Xamarin.Essentials/Permissions/Permissions.android.cs b/Xamarin.Essentials/Permissions/Permissions.android.cs index 29793f80b..e80105ae7 100644 --- a/Xamarin.Essentials/Permissions/Permissions.android.cs +++ b/Xamarin.Essentials/Permissions/Permissions.android.cs @@ -271,6 +271,30 @@ public override (string androidPermission, bool isRuntime)[] RequiredPermissions return permissions.ToArray(); } } + +#if __ANDROID_29__ + public override async Task RequestAsync() + { + // Check status before requesting first + if (await CheckStatusAsync() == PermissionStatus.Granted) + return PermissionStatus.Granted; + + if (Platform.HasApiLevel(30)) + { + var permissionResult = await new LocationWhenInUse().RequestAsync(); + if (permissionResult == PermissionStatus.Denied) + return PermissionStatus.Denied; + + var result = await DoRequest(new string[] { Manifest.Permission.AccessBackgroundLocation }); + if (!result.GrantResults.All(x => x == Permission.Granted)) + permissionResult = PermissionStatus.Restricted; + + return permissionResult; + } + + return await base.RequestAsync(); + } +#endif } public partial class Maps : BasePlatformPermission