-
Notifications
You must be signed in to change notification settings - Fork 505
Conversation
… mor than a few seconds
@@ -9,6 +9,10 @@ namespace Microsoft.Caboodle | |||
{ | |||
internal static partial class Permissions | |||
{ | |||
static readonly object locationLocker = new object(); | |||
static CLLocationManager locationManager; | |||
static TaskCompletionSource<PermissionStatus> locationTcs; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs a set of eyes here 👀 - and on all the changes to make the permissions not just disappear in a second.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am locking and making sure that there is only a single location manager for all permissions, but is this necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I also saw a constructor for TaskCompletionSource
that takes object state
... I have never used that, but maybe there is some way to use that to keep the listener alive... will investigate, but comments will help.
@@ -31,5 +32,18 @@ internal static string Md5Hash(string input) | |||
return hash.ToString(); | |||
#endif | |||
} | |||
|
|||
internal static CancellationToken TimeoutToken(CancellationToken cancellationToken, TimeSpan timeout) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A util function that will auto-cancel a token after a timeout
{ | ||
await Permissions.RequireAsync(PermissionType.LocationWhenInUse).ConfigureAwait(false); | ||
|
||
var manager = new CLLocationManager(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This location manager is different to the one used in permissions. Need to review if this gets GC'ed unexpectedly.
|
||
var manager = new CLLocationManager(); | ||
|
||
var tcs = new TaskCompletionSource<CLLocation>(manager); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep a reference to the manager for the duration of the task... this needs 👀 and 💭
var locationManager = new CLLocationManager(); | ||
var manager = new CLLocationManager(); | ||
|
||
var tcs = new TaskCompletionSource<PermissionStatus>(manager); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Keep a reference to the manager for the duration of the task... this needs 👀 and 💭
With regards to a fail-fast case, this is a bit harder to implement. To be realistic, almost all devices nowadays have a GPS or some basic location system. In most cases there are 2 alternatives: no permission and disabled. UWP is fun with a more async model. The locator has to init and then updates a status property during that process. Also, when the GPS is off, the iOS appears to always have a GPS, and they have a property for detecting whether it enabled or not. Android appears to be like UWP in that you have to get permission in order to see what GPS/location devices are available. If there are no device available, then all have been disabled and thus no location data. If there are no devices, it could mean that all are disabled, or that there just simply are none. |
Just looking at the implementations, we have to handle the cases of the location devices being disabled or permissions revoked during a request. |
Honestly this is more reason to consider play services location since it helps deal with getting the user to enable location. Not to mention it's a much better API to deal with. I'm not excited about the dependency it would add and we would still need to fall back on the old api but something to consider! |
I have a version 5 of the geolocator plugin, it does help a bit on Android that is sure, but yeah some tricky parts with google play as a dependency :( What I did on Android was detect if it had any providers available to determine if it was active or not. The gotcha is that you need to have permissions set to query providers :) iOS is easy peasy:
UWP is a bundle of terrible to be honest with you see here: jamesmontemagno/GeolocatorPlugin#144 |
I am wondering if it is best just to assume always on... make the request and throw the exception if it fails... we are exception first.. the question is does that work properly. On iOS/Android we can check permissions first... UWP is a pain in the butt a bit. |
|
||
static async Task<Location> PlatformLastKnownLocationAsync() | ||
{ | ||
await Permissions.RequireAsync(PermissionType.LocationWhenInUse).ConfigureAwait(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we remove the configureawait for now throughout. I am still worried about that stuff.
var manager = new CLLocationManager(); | ||
var location = manager.Location; | ||
|
||
return new Location |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you make this an extension method to convert like i do for geocoding. It may already be there actually.
{ | ||
try | ||
{ | ||
return new DateTimeOffset((DateTime)timestamp); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should put these in platform helpers
if (location?.Coordinate == null) | ||
return null; | ||
|
||
return new Location |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here for extension methods
@@ -84,6 +85,9 @@ public static void BeginInvokeOnMainThread(Action action) | |||
|
|||
internal static ClipboardManager ClipboardManager => | |||
Application.Context.GetSystemService(Context.ClipboardService) as ClipboardManager; | |||
|
|||
internal static LocationManager LocationManager => | |||
(LocationManager)Application.Context.GetSystemService(Context.LocationService); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we use "as" for null checks along the way.
@@ -53,5 +53,7 @@ | |||
<string>Caboodle.Samples</string> | |||
<key>CFBundleShortVersionString</key> | |||
<string>1.0</string> | |||
<key>NSLocationWhenInUseUsageDescription</key> | |||
<string>Access to your location is required for cool things to happen!</string> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
tab this puppy over
Yeah I say we just check for hardware and not try and query enabled status here. I think it's not expected to have a UI asking for permission when checking if it's supported. Also we don't really expose the is supported yet anyway so there really is no code path that wouldn't check for permission anyway. Tl;dr let's check for hardware only. |
There is something I noticed on iOS is that the test will fail because the location manager has some arb requirements about where locations are started from:
If the location is requested from a background thread, then the task will wait indefinitely since there is no feedback whatsoever. I have updated the tests to call the location manager from the UI thread and I throw an exception in the request code, but this requirement may/may not be acceptable for us. There are some steps that we can take:
|
I went with option 3. |
// so just use the main loop | ||
Platform.BeginInvokeOnMainThread(() => | ||
{ | ||
var manager = new CLLocationManager(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We create and start the location manger on the main thread:
- because we need an active run loop
- it is non-blocking
- it does not affect the actual call threading system
If we are on the main thread already, this basically is just a straight execution as if nothing special was happening.
}); | ||
|
||
// still wait and return on the background thread | ||
var clLocation = await tcs.Task; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We await the location manager:
- we still wait the current thread until the main thread gets back to us
- we continue on the background thread
build |
<uses-permission android:name="android.permission.CAMERA" /> | ||
<uses-permission android:name="android.permission.FLASHLIGHT" /> | ||
<uses-permission android:name="android.permission.INTERNET" /> | ||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need Read/Write for anything?
{ | ||
try | ||
{ | ||
locationManager.RemoveUpdates(listener); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we really ignore this?
break; | ||
} | ||
|
||
return locationManager.GetBestProvider(criteria, true) ?? locationManager.GetProviders(true).First(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be FirstorDefault?
{ | ||
await Permissions.RequireAsync(PermissionType.LocationWhenInUse); | ||
|
||
var manager = new CLLocationManager(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have this on the main thread or doesn't matter??
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It does not appear to matter.
|
||
wasRaised = true; | ||
|
||
var location = locations.LastOrDefault(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are we assuming these are ordered by priority? Accuracy?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
According to the docs:
The objects in the array are organized in the order in which they occurred. Therefore, the most recent location update is at the end of the array.
https://developer.apple.com/documentation/corelocation/cllocationmanagerdelegate/1423615-locationmanager
Description of Change
Adds Geolocation API's
API Changes
PR Checklist