using System; using Windows.Devices.Geolocation; using WPCordovaClassLib.Cordova; using WPCordovaClassLib.Cordova.Commands; using WPCordovaClassLib.Cordova.JSON; using System.Diagnostics; namespace Cordova.Extension.Commands { public class BackgroundGeoLocation : BaseCommand, IBackgroundGeoLocation { private string ConfigureCallbackToken { get; set; } private BackgroundGeoLocationOptions BackgroundGeoLocationOptions { get; set; } /// /// Geolocator and RunningInBackground are required properties to run in background /// For more information read http://msdn.microsoft.com/library/windows/apps/jj662935(v=vs.105).aspx /// public static Geolocator Geolocator { get; set; } public static bool RunningInBackground { get; set; } /// /// When start() is fired immediate after configure() in javascript, configure may not be finished yet, IsConfigured and IsConfiguring are used to keep track of this /// private bool IsConfigured { get; set; } private bool IsConfiguring { get; set; } public BackgroundGeoLocation() { IsConfiguring = false; IsConfigured = false; } public void configure(string args) { IsConfiguring = true; ConfigureCallbackToken = CurrentCommandCallbackId; RunningInBackground = false; BackgroundGeoLocationOptions = this.ParseBackgroundGeoLocationOptions(args); IsConfigured = BackgroundGeoLocationOptions.ParsingSucceeded; IsConfiguring = false; } private BackgroundGeoLocationOptions ParseBackgroundGeoLocationOptions(string configureArgs) { var parsingSucceeded = true; var options = JsonHelper.Deserialize(configureArgs); double stationaryRadius; double distanceFilter; UInt32 locationTimeout; UInt32 desiredAccuracy; bool debug; if (!double.TryParse(options[3], out stationaryRadius)) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, string.Format("Invalid value for stationaryRadius:{0}", options[3]))); parsingSucceeded = false; } if (!double.TryParse(options[4], out distanceFilter)) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, string.Format("Invalid value for distanceFilter:{0}", options[4]))); parsingSucceeded = false; } if (!UInt32.TryParse(options[5], out locationTimeout)) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, string.Format("Invalid value for locationTimeout:{0}", options[5]))); parsingSucceeded = false; } if (!UInt32.TryParse(options[6], out desiredAccuracy)) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, string.Format("Invalid value for desiredAccuracy:{0}", options[6]))); parsingSucceeded = false; } if (!bool.TryParse(options[7], out debug)) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, string.Format("Invalid value for debug:{0}", options[7]))); parsingSucceeded = false; } return new BackgroundGeoLocationOptions { Url = options[1], StationaryRadius = stationaryRadius, DistanceFilterInMeters = distanceFilter, LocationTimeoutInMilliseconds = locationTimeout, DesiredAccuracyInMeters = desiredAccuracy, Debug = debug, ParsingSucceeded = parsingSucceeded }; } public void start(string nothing) { while (!IsConfigured && IsConfiguring) { // Wait for configure() to complete... } if (!IsConfigured || !BackgroundGeoLocationOptions.ParsingSucceeded) { DispatchCommandResult(new PluginResult(PluginResult.Status.INVALID_ACTION, "Cannot start: Run configure() with proper values!")); stop(); return; } StopGeolocatorIfActive(); Geolocator = new Geolocator { // Default: 50 meters MovementThreshold = BackgroundGeoLocationOptions.DistanceFilterInMeters, // JS Interface takes seconds, MS takes miliseconds, default 60 seconds ReportInterval = BackgroundGeoLocationOptions.LocationTimeoutInMilliseconds * 1000, // In our case this property has always a value, if left empty or below zero the default will be 100 meter but can be overridden via parameter DesiredAccuracy DesiredAccuracyInMeters = BackgroundGeoLocationOptions.DesiredAccuracyInMeters }; Geolocator.PositionChanged += OnGeolocatorOnPositionChanged; RunningInBackground = true; } private void OnGeolocatorOnPositionChanged(Geolocator sender, PositionChangedEventArgs configureCallbackTokenargs) { if (Geolocator.LocationStatus == PositionStatus.Disabled || Geolocator.LocationStatus == PositionStatus.NotAvailable) { DispatchMessage(PluginResult.Status.ERROR, string.Format("Cannot start: LocationStatus/PositionStatus: {0}! {1}", Geolocator.LocationStatus, IsConfigured), true, ConfigureCallbackToken); return; } var callbackJsonResult = configureCallbackTokenargs.Position.Coordinate.ToJson(); if (BackgroundGeoLocationOptions.Debug) { DebugAudioNotifier.GetDebugAudioNotifier().PlaySound(DebugAudioNotifier.Tone.High, TimeSpan.FromSeconds(3)); Debug.WriteLine("PositionChanged token{0}, Coordinates: {1}", ConfigureCallbackToken, callbackJsonResult); } DispatchMessage(PluginResult.Status.OK, callbackJsonResult, true, ConfigureCallbackToken); } public void stop() { RunningInBackground = false; StopGeolocatorIfActive(); DispatchCommandResult(new PluginResult(PluginResult.Status.OK)); } private void StopGeolocatorIfActive() { if (Geolocator == null) return; Geolocator.PositionChanged -= OnGeolocatorOnPositionChanged; Geolocator = null; } public void finish() { DispatchCommandResult(new PluginResult(PluginResult.Status.NO_RESULT)); } public void onPaceChange(bool isMoving) { throw new NotImplementedException(); } public void setConfig(string setConfigArgs) { if (string.IsNullOrWhiteSpace(setConfigArgs)) { DispatchCommandResult(new PluginResult(PluginResult.Status.INVALID_ACTION, "Cannot set config because of an empty input")); return; } var parsingSucceeded = true; var options = JsonHelper.Deserialize(setConfigArgs); double stationaryRadius; double distanceFilter; UInt32 locationTimeout; UInt32 desiredAccuracy; if (!double.TryParse(options[0], out stationaryRadius)) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, string.Format("Invalid value for stationaryRadius:{0}", options[2]))); parsingSucceeded = false; } if (!double.TryParse(options[1], out distanceFilter)) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, string.Format("Invalid value for distanceFilter:{0}", options[3]))); parsingSucceeded = false; } if (!UInt32.TryParse(options[2], out locationTimeout)) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, string.Format("Invalid value for locationTimeout:{0}", options[4]))); parsingSucceeded = false; } if (!UInt32.TryParse(options[3], out desiredAccuracy)) { DispatchCommandResult(new PluginResult(PluginResult.Status.JSON_EXCEPTION, string.Format("Invalid value for desiredAccuracy:{0}", options[5]))); parsingSucceeded = false; } if (!parsingSucceeded) return; BackgroundGeoLocationOptions.StationaryRadius = stationaryRadius; BackgroundGeoLocationOptions.DistanceFilterInMeters = distanceFilter; BackgroundGeoLocationOptions.LocationTimeoutInMilliseconds = locationTimeout * 1000; BackgroundGeoLocationOptions.DesiredAccuracyInMeters = desiredAccuracy; DispatchCommandResult(new PluginResult(PluginResult.Status.OK)); } public void getStationaryLocation() { throw new NotImplementedException(); } private void DispatchMessage(PluginResult.Status status, string message, bool keepCallback, string callBackId) { var pluginResult = new PluginResult(status, message) { KeepCallback = keepCallback }; DispatchCommandResult(pluginResult, callBackId); } } }