0

I have a Xamarin.Forms application that supports UWP, iOS, and Android. Specifically, I am now testing my app on Android emulator. For getting location, I am using Xamarin.Essentials. Here are pieces of my code:

In a PageModel:

                    bu = await GeolocationSrvc.GetBusinessUnitAsync();

And here is the implementation of the method above:

    public static async Task<BusinessUnits> GetBusinessUnitAsync()
    {
        BusinessUnits bu = BusinessUnits.Aus;

        try
        {
            Location location = await GetLocation().ConfigureAwait(false);

            IEnumerable<Placemark> placemarks = await Geocoding.GetPlacemarksAsync(location);
            Placemark placemark = placemarks?.FirstOrDefault();
            string countryCode = placemark?.CountryCode;

            switch (countryCode)
            {
                case "AQ":
                case "AU":
                case "NZ":
                    bu = BusinessUnits.Aus;
                    break;
                default:
                    bu = BusinessUnits.NA;
                    break;
            }
        }
        catch (Exception)
        {
            throw;
        }

        return bu;
    }

    private static Task<Location> GetLocation()
    {
        GeolocationRequest request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10));
        TaskCompletionSource<Location> locationTaskCompletionSource = new TaskCompletionSource<Location>();

        Device.BeginInvokeOnMainThread(async () =>
        {
            locationTaskCompletionSource.SetResult(await Geolocation.GetLocationAsync(request));
        });

        return locationTaskCompletionSource.Task;
    }

When the execution comes to

locationTaskCompletionSource.SetResult(await Geolocation.GetLocationAsync(request));

I am asked if I want to allow the app to get my location. If I press Yes, it works as expected. But if I press No, the location is never returned (not even null), the following code is never executed. I expected in case of answering No to use default value set in

BusinessUnits bu = BusinessUnits.Aus;

But it does not happen.

halfer
  • 19,824
  • 17
  • 99
  • 186
David Shochet
  • 5,035
  • 11
  • 57
  • 105

2 Answers2

1

You're not setting the Exception of your TaskCompletionSource object:

private static Task<Location> GetLocation()
{
    GeolocationRequest request = new GeolocationRequest(GeolocationAccuracy.Medium, TimeSpan.FromSeconds(10));
    TaskCompletionSource<Location> locationTaskCompletionSource = new TaskCompletionSource<Location>();

    Device.BeginInvokeOnMainThread(async () =>
    {
        try
        {
            locationTaskCompletionSource.SetResult(await Geolocation.GetLocationAsync(request));
        }
        catch(Exception exception)
        {
            locationTaskCompletionSource.SetException(exception);
            locationTaskCompletionSource.SetResult(null);
        }
    });

    return locationTaskCompletionSource.Task;
}
Roubachof
  • 3,351
  • 3
  • 23
  • 34
1

Alternate approach would be to check for location permission before hand using a simple dependency service for each platform.

If the permission is granted, then continue with location fetch. Else prompt user to get permission.

For ex. Android implementation to check location permission:

public bool IsLocationPermissionGranted()
{
    if (ContextCompat.CheckSelfPermission(Application.Context, 
    Manifest.Permission.AccessFineLocation) == Permission.Granted)
    {
        return true;
    }
    return false;
}
Rajesh Sonar
  • 279
  • 3
  • 11
  • Thank you for your suggestion. It works, so now I can know if the permission is there. Could you please tell me how I can set permission to true if the user chooses so? Also, how can I prevent from asking it again automatically when Geolocation.GetLocationAsync() is called? And maybe you have implementation for iOS and UWP? Sorry for asking for so many things. – David Shochet Aug 14 '19 at 20:05
  • Taking user permission is generally a one time task unless user decides to revoke it manually from app settings. Once user has granted the permission, it is persisted by OS, so you don't need to keep track of it. But you do need to check it each time before initiating location fetch request. – Rajesh Sonar Aug 14 '19 at 20:17
  • I understand that. But if the permission check returns false, I display a message asking the user for permission, and he says yes, how can I set it to let the OS know? – David Shochet Aug 14 '19 at 20:23
  • When user says yes, OS automatically registers that for your application. So next time you check, you get Permission.Granted. – Rajesh Sonar Aug 14 '19 at 20:25
  • There is a good plugin to check permission in cross platform way https://github.com/jamesmontemagno/permissionsplugin – Rajesh Sonar Aug 14 '19 at 20:25
  • I thought you meant that I should program asking the user for permission explicitly, and do something with the answer. But I guess I did not understand you. After the check returns true or false, how will the OS know to ask the user? – David Shochet Aug 14 '19 at 20:34
  • 1
    Ahh...now I get you. You are correct, If permission is not already granted, then you will have to ask it. For that, you can write another function in your dependency service. OR you can use the plugin which I mentioned earlier. This plugin handles permission checking as well as requesting. And supports all three platforms you are targeting. Please go through its documentation. – Rajesh Sonar Aug 14 '19 at 20:47
  • Thank you, the plugin seems to be working with Android. But with UWP, CrossPermissions.Current.CheckPermissionStatusAsync(Location) throws an exception "Server Execution failed". Do you have any explanation? – David Shochet Aug 15 '19 at 13:49