From f185ef419f76fa807052747611ca7c4b53a54236 Mon Sep 17 00:00:00 2001 From: Jonathan Dick Date: Tue, 10 Apr 2018 15:25:48 -0400 Subject: [PATCH] GH-7: Geolocation API's (#132) * First pass at Android Geolocation impl * No need for nullables * Added the geolocation sample * Added the UWP geolocation implementation * Getting some of the iOS in * Use a global location manager so that the permissions popup lives for mor than a few seconds * Check before asking on iOS * Finished the iOS implementation and tweak a few things * Remove the unsused using * Don't use locking and static fields, use the TCS state * Keep the manager alive for the duration of the method * Use platform specific accuracy * Moving files after merge * Changing namespaces * Removed the `ConfigureAwait(false)` instances * Use extension methods * tabs not spaces * Added sopme docs * More docs * Added some tests (that can't run yet) * Enabling the tests for CI after adding attributes * Added the iOS permissions text to the tests * iOS has opinions when it comes to locations * Starting the location manager on the main thread, but return on the original thread * We just need to call the constructor in the main thread * Added all the permissions to the manifest for the samples and the tests * Android has looper issues * Location is now a declared permission * Changes based on feedback. * Cleanup iOS Permissions. Must have static location manager around. * The stylish copper got us again --- .../Properties/AndroidManifest.xml | 6 + .../Properties/AssemblyInfo.cs | 4 - .../DeviceTests.Shared.projitems | 1 + .../DeviceTests.Shared/Geolocation_Tests.cs | 74 +++++++ .../DeviceTests.Shared/Permissions_Tests.cs | 29 +-- DeviceTests/DeviceTests.iOS/Info.plist | 2 + .../Properties/AndroidManifest.xml | 5 +- .../Properties/AssemblyInfo.cs | 4 - Samples/Samples.UWP/Package.appxmanifest | 1 + Samples/Samples.iOS/Info.plist | 2 + Samples/Samples/View/GeolocationPage.xaml | 35 ++++ Samples/Samples/View/GeolocationPage.xaml.cs | 10 + .../Samples/ViewModel/GeolocationViewModel.cs | 97 +++++++++ Samples/Samples/ViewModel/HomeViewModel.cs | 1 + .../Geolocation/Geolocation.android.cs | 194 ++++++++++++++++++ .../Geolocation/Geolocation.ios.cs | 92 +++++++++ .../Geolocation/Geolocation.netstandard.cs | 15 ++ .../Geolocation/Geolocation.shared.cs | 22 ++ .../Geolocation/Geolocation.uwp.cs | 54 +++++ .../Geolocation/GeolocationRequest.ios.cs | 32 +++ .../Geolocation/GeolocationRequest.shared.cs | 57 +++++ .../Geolocation/GeolocationRequest.uwp.cs | 31 +++ .../Permissions/Permissions.ios.cs | 61 ++---- .../Platform/Platform.android.cs | 6 +- .../SecureStorage/SecureStorage.uwp.cs | 3 + Xamarin.Essentials/Types/Location.shared.cs | 2 + .../Types/LocationExtensions.android.cs | 15 +- .../Types/LocationExtensions.ios.cs | 29 ++- .../Types/LocationExtensions.uwp.cs | 17 +- .../Types/Shared/Utils.shared.cs | 14 ++ .../AccelerometerChangedEventArgs.xml | 2 +- docs/en/Xamarin.Essentials/Geolocation.xml | 94 +++++++++ .../GeolocationAccuracy.xml | 92 +++++++++ .../Xamarin.Essentials/GeolocationRequest.xml | 97 +++++++++ docs/en/Xamarin.Essentials/Location.xml | 18 +- docs/en/index.xml | 7 +- 36 files changed, 1140 insertions(+), 85 deletions(-) create mode 100644 DeviceTests/DeviceTests.Shared/Geolocation_Tests.cs create mode 100644 Samples/Samples/View/GeolocationPage.xaml create mode 100644 Samples/Samples/View/GeolocationPage.xaml.cs create mode 100644 Samples/Samples/ViewModel/GeolocationViewModel.cs create mode 100644 Xamarin.Essentials/Geolocation/Geolocation.android.cs create mode 100644 Xamarin.Essentials/Geolocation/Geolocation.ios.cs create mode 100644 Xamarin.Essentials/Geolocation/Geolocation.netstandard.cs create mode 100644 Xamarin.Essentials/Geolocation/Geolocation.shared.cs create mode 100644 Xamarin.Essentials/Geolocation/Geolocation.uwp.cs create mode 100644 Xamarin.Essentials/Geolocation/GeolocationRequest.ios.cs create mode 100644 Xamarin.Essentials/Geolocation/GeolocationRequest.shared.cs create mode 100644 Xamarin.Essentials/Geolocation/GeolocationRequest.uwp.cs create mode 100644 docs/en/Xamarin.Essentials/Geolocation.xml create mode 100644 docs/en/Xamarin.Essentials/GeolocationAccuracy.xml create mode 100644 docs/en/Xamarin.Essentials/GeolocationRequest.xml diff --git a/DeviceTests/DeviceTests.Android/Properties/AndroidManifest.xml b/DeviceTests/DeviceTests.Android/Properties/AndroidManifest.xml index ba9a922..a854f90 100644 --- a/DeviceTests/DeviceTests.Android/Properties/AndroidManifest.xml +++ b/DeviceTests/DeviceTests.Android/Properties/AndroidManifest.xml @@ -1,7 +1,13 @@  + + + + + + \ No newline at end of file diff --git a/DeviceTests/DeviceTests.Android/Properties/AssemblyInfo.cs b/DeviceTests/DeviceTests.Android/Properties/AssemblyInfo.cs index f00869a..799705e 100644 --- a/DeviceTests/DeviceTests.Android/Properties/AssemblyInfo.cs +++ b/DeviceTests/DeviceTests.Android/Properties/AssemblyInfo.cs @@ -28,7 +28,3 @@ using Android.App; // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] - -// Add some common permissions, these can be removed if not needed -[assembly: UsesPermission(Android.Manifest.Permission.Internet)] -[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)] diff --git a/DeviceTests/DeviceTests.Shared/DeviceTests.Shared.projitems b/DeviceTests/DeviceTests.Shared/DeviceTests.Shared.projitems index 237fa6b..43f7a9d 100644 --- a/DeviceTests/DeviceTests.Shared/DeviceTests.Shared.projitems +++ b/DeviceTests/DeviceTests.Shared/DeviceTests.Shared.projitems @@ -20,6 +20,7 @@ + diff --git a/DeviceTests/DeviceTests.Shared/Geolocation_Tests.cs b/DeviceTests/DeviceTests.Shared/Geolocation_Tests.cs new file mode 100644 index 0000000..0487911 --- /dev/null +++ b/DeviceTests/DeviceTests.Shared/Geolocation_Tests.cs @@ -0,0 +1,74 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Xamarin.Essentials; +using Xunit; + +namespace DeviceTests +{ + // TEST NOTES: + // - a human needs to accept permissions + public class Geolocation_Tests + { + [Fact] + [Trait(Traits.InteractionType, Traits.InteractionTypes.Human)] + public async Task Get_LastKnownLocation_Is_Something() + { + var location = await Geolocation.GetLastKnownLocationAsync(); + + Assert.NotNull(location); + + Assert.True(location.Accuracy > 0); + Assert.NotEqual(0.0, location.Latitude); + Assert.NotEqual(0.0, location.Longitude); + + Assert.NotEqual(DateTimeOffset.MaxValue, location.TimestampUtc); + Assert.NotEqual(DateTimeOffset.MinValue, location.TimestampUtc); + + // before right now, but after yesterday + Assert.True(location.TimestampUtc < DateTimeOffset.UtcNow); + Assert.True(location.TimestampUtc > DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(1))); + } + + [Fact] + [Trait(Traits.InteractionType, Traits.InteractionTypes.Human)] + public async Task Get_Location_Is_Something() + { + var location = await Geolocation.GetLocationAsync(); + + Assert.NotNull(location); + + Assert.True(location.Accuracy > 0); + Assert.NotEqual(0.0, location.Latitude); + Assert.NotEqual(0.0, location.Longitude); + + Assert.NotEqual(DateTimeOffset.MaxValue, location.TimestampUtc); + Assert.NotEqual(DateTimeOffset.MinValue, location.TimestampUtc); + + // before right now, but after yesterday + Assert.True(location.TimestampUtc < DateTimeOffset.UtcNow); + Assert.True(location.TimestampUtc > DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(1))); + } + + [Fact] + [Trait(Traits.InteractionType, Traits.InteractionTypes.Human)] + public async Task Get_Location_With_Request_Is_Something() + { + var request = new GeolocationRequest(GeolocationAccuracy.Best); + var location = await Geolocation.GetLocationAsync(request); + + Assert.NotNull(location); + + Assert.True(location.Accuracy > 0); + Assert.NotEqual(0.0, location.Latitude); + Assert.NotEqual(0.0, location.Longitude); + + Assert.NotEqual(DateTimeOffset.MaxValue, location.TimestampUtc); + Assert.NotEqual(DateTimeOffset.MinValue, location.TimestampUtc); + + // before right now, but after yesterday + Assert.True(location.TimestampUtc < DateTimeOffset.UtcNow); + Assert.True(location.TimestampUtc > DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(1))); + } + } +} diff --git a/DeviceTests/DeviceTests.Shared/Permissions_Tests.cs b/DeviceTests/DeviceTests.Shared/Permissions_Tests.cs index 793125c..e94e879 100644 --- a/DeviceTests/DeviceTests.Shared/Permissions_Tests.cs +++ b/DeviceTests/DeviceTests.Shared/Permissions_Tests.cs @@ -5,10 +5,6 @@ using System.Threading.Tasks; using Xamarin.Essentials; using Xunit; -#if __ANDROID__ -[assembly: Android.App.UsesPermission(Android.Manifest.Permission.BatteryStats)] -#endif - namespace DeviceTests { public class Permissions_Tests @@ -16,23 +12,12 @@ namespace DeviceTests [Theory] [InlineData(PermissionType.Battery)] [InlineData(PermissionType.NetworkState)] + [InlineData(PermissionType.LocationWhenInUse)] internal void Ensure_Declared(PermissionType permission) { Permissions.EnsureDeclared(permission); } - [Theory] - [InlineData(PermissionType.LocationWhenInUse)] - internal void Ensure_Declared_Throws(PermissionType permission) - { - if (DeviceInfo.Platform == DeviceInfo.Platforms.UWP) - { - return; - } - - Assert.Throws(() => Permissions.EnsureDeclared(permission)); - } - [Theory] [InlineData(PermissionType.Battery, PermissionStatus.Granted)] [InlineData(PermissionType.NetworkState, PermissionStatus.Granted)] @@ -43,18 +28,6 @@ namespace DeviceTests Assert.Equal(expectedStatus, status); } - [Theory] - [InlineData(PermissionType.LocationWhenInUse)] - internal Task Check_Status_Throws(PermissionType permission) - { - if (DeviceInfo.Platform == DeviceInfo.Platforms.UWP) - { - return Task.CompletedTask; - } - - return Assert.ThrowsAsync(async () => await Permissions.CheckStatusAsync(permission)); - } - [Theory] [InlineData(PermissionType.Battery, PermissionStatus.Granted)] [InlineData(PermissionType.NetworkState, PermissionStatus.Granted)] diff --git a/DeviceTests/DeviceTests.iOS/Info.plist b/DeviceTests/DeviceTests.iOS/Info.plist index 440dd84..49406d8 100644 --- a/DeviceTests/DeviceTests.iOS/Info.plist +++ b/DeviceTests/DeviceTests.iOS/Info.plist @@ -53,5 +53,7 @@ DeviceTests CFBundleShortVersionString 1.0.1.0 + NSLocationWhenInUseUsageDescription + Access to your location is required for cool things to happen! diff --git a/Samples/Samples.Android/Properties/AndroidManifest.xml b/Samples/Samples.Android/Properties/AndroidManifest.xml index 85e0ea7..5b99da4 100644 --- a/Samples/Samples.Android/Properties/AndroidManifest.xml +++ b/Samples/Samples.Android/Properties/AndroidManifest.xml @@ -1,10 +1,13 @@  + + - + + \ No newline at end of file diff --git a/Samples/Samples.Android/Properties/AssemblyInfo.cs b/Samples/Samples.Android/Properties/AssemblyInfo.cs index 5394d99..0117be5 100644 --- a/Samples/Samples.Android/Properties/AssemblyInfo.cs +++ b/Samples/Samples.Android/Properties/AssemblyInfo.cs @@ -27,7 +27,3 @@ using Android.App; // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] - -// Add some common permissions, these can be removed if not needed -[assembly: UsesPermission(Android.Manifest.Permission.Internet)] -[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)] diff --git a/Samples/Samples.UWP/Package.appxmanifest b/Samples/Samples.UWP/Package.appxmanifest index 0f07c71..7e0d9e0 100644 --- a/Samples/Samples.UWP/Package.appxmanifest +++ b/Samples/Samples.UWP/Package.appxmanifest @@ -24,5 +24,6 @@ + \ No newline at end of file diff --git a/Samples/Samples.iOS/Info.plist b/Samples/Samples.iOS/Info.plist index 6d4ae7d..8817e50 100644 --- a/Samples/Samples.iOS/Info.plist +++ b/Samples/Samples.iOS/Info.plist @@ -53,5 +53,7 @@ Xamarin.Essentials CFBundleShortVersionString 1.0 + NSLocationWhenInUseUsageDescription + Access to your location is required for cool things to happen! diff --git a/Samples/Samples/View/GeolocationPage.xaml b/Samples/Samples/View/GeolocationPage.xaml new file mode 100644 index 0000000..388c1d7 --- /dev/null +++ b/Samples/Samples/View/GeolocationPage.xaml @@ -0,0 +1,35 @@ + + + + + + + +