c92fae52de562f2b3fc34499c2b5420b36b75879
[platform/core/csapi/tizenfx.git] / src / Tizen.Location / Tizen.Location / Locator.cs
1 // Copyright 2016 by Samsung Electronics, Inc.,
2 //
3 // This software is the confidential and proprietary information
4 // of Samsung Electronics, Inc. ("Confidential Information"). You
5 // shall not disclose such Confidential Information and shall use
6 // it only in accordance with the terms of the license agreement
7 // you entered into with Samsung.
8
9 using System;
10 using System.Collections.Generic;
11 using System.Threading.Tasks;
12 using Tizen.Internals.Errors;
13 using System.Runtime.InteropServices;
14
15 namespace Tizen.Location
16 {
17     static internal class Globals
18     {
19         internal const string LogTag = "Tizen.Location";
20     }
21
22     /// <summary>
23     /// A class which contains the functionality for obtaining geographical infomation and setting boundary condition.
24     /// Notifications on events like service becoming enabled or disabled, new position data being available
25     /// and others can also be acquired.
26     /// </summary>
27     public class Locator:IDisposable
28     {
29         private int _interval = 1;
30         private int _stayInterval = 120;
31         private double _distance = 120.0;
32         private bool _isEnableMock;
33         private IntPtr _handle;
34         private LocationType _locationType;
35         private Location _location = null;
36         private bool _disposed = false;
37         private bool _isStarted = false;
38         private static Locator s_locatorReference;
39         private int _requestId = 0;
40         private Dictionary<IntPtr, Interop.LocatorEvent.LocationUpdatedCallback> _callback_map = new Dictionary<IntPtr, Interop.LocatorEvent.LocationUpdatedCallback>();
41
42         private EventHandler<ZoneChangedEventArgs> _zoneChanged;
43         private EventHandler<ServiceStateChangedEventArgs> _serviceStateChanged;
44         private EventHandler<SettingChangedEventArgs> _settingChanged;
45         private EventHandler<LocationChangedEventArgs> _distanceBasedLocationChanged;
46         private EventHandler<LocationChangedEventArgs> _locationChanged;
47
48         /// <summary>
49         /// The constructor of Locator class.
50         /// </summary>
51         /// <param name="locationType"> The back-end positioning method to be used for LBS.</param>
52         public Locator(LocationType locationType)
53         {
54             Log.Info(Globals.LogTag, "Locator Constructor");
55             int ret = Interop.Locator.Create((int)locationType, out _handle);
56             if (((LocationError)ret != LocationError.None))
57             {
58                 Log.Error(Globals.LogTag, "Error creating Location Manager," + (LocationError)ret);
59                 LocationErrorFactory.ThrowLocationException(ret);
60             }
61             _location = new Location();
62             _locationType = locationType;
63         }
64
65         /// <summary>
66         /// The destructor of Locator class.
67         /// </summary>
68         ~Locator()
69         {
70             Dispose(false);
71         }
72
73         /// <summary>
74         /// The time interval between callback updates.
75         /// Should be in the range [1~120] seconds.
76         /// </summary>
77         public int Interval
78         {
79             get
80             {
81                 Log.Info(Globals.LogTag, "Getting the Callback Interval");
82                 return _interval;
83             }
84             set
85             {
86                 Log.Info(Globals.LogTag, "Setting the Callback Interval");
87                 if (value > 0 && value <= 120)
88                 {
89                     _interval = value;
90                     if (_locationChanged != null)
91                     {
92                         SetLocationChangedCallback();
93                     }
94                 }
95                 else
96                 {
97                     Log.Error(Globals.LogTag, "Error setting Callback Interval");
98                     LocationErrorFactory.ThrowLocationException((int)LocationError.InvalidParameter);
99                 }
100             }
101         }
102
103         /// <summary>
104         /// The time interval between Distance based location callback updates.
105         /// Should be in the range [1~120] seconds.
106         /// </summary>
107         public int StayInterval
108         {
109             get
110             {
111                 Log.Info(Globals.LogTag, "Getting the StayInterval");
112                 return _stayInterval;
113             }
114             set
115             {
116                 Log.Info(Globals.LogTag, "Setting the StayInterval");
117                 if (value > 0 && value <= 120)
118                 {
119                     _stayInterval = value;
120                     if (_distanceBasedLocationChanged != null)
121                     {
122                         SetDistanceBasedLocationChangedCallback();
123                     }
124                 }
125                 else
126                 {
127                     Log.Error(Globals.LogTag, "Error Setting the StayInterval");
128                     LocationErrorFactory.ThrowLocationException((int)LocationError.InvalidParameter);
129                 }
130             }
131         }
132
133         /// <summary>
134         /// The distance between callback updates.
135         /// Should be in the range [1-120] meters.
136         /// </summary>
137         public double Distance
138         {
139             get
140             {
141                 Log.Info(Globals.LogTag, "Getting the Distance Interval");
142                 return _distance;
143             }
144             set
145             {
146                 Log.Info(Globals.LogTag, "Setting the Distance Interval");
147                 if (value > 0 && value <= 120)
148                 {
149                     _distance = value;
150                     if (_distanceBasedLocationChanged != null)
151                     {
152                         SetDistanceBasedLocationChangedCallback();
153                     }
154                 }
155                 else
156                 {
157                     Log.Error(Globals.LogTag, "Error Setting the Distance");
158                     LocationErrorFactory.ThrowLocationException((int)LocationError.InvalidParameter);
159                 }
160             }
161         }
162
163         /// <summary>
164         /// Gets the Location object.
165         /// </summary>
166         public Location Location
167         {
168             get
169             {
170                 Log.Info(Globals.LogTag, "Getting location details");
171                 return _location;
172            }
173         }
174
175         /// <summary>
176         /// Gets the type used to obtain Location data.
177         /// </summary>
178         public LocationType LocationType
179         {
180             get
181             {
182                 Log.Info(Globals.LogTag, "Getting LocationType");
183                 return _locationType;
184             }
185         }
186
187         /// <summary>
188         /// Gets the status whether mock location is enabled or not.
189         /// </summary>
190         public bool EnableMock
191         {
192             get
193             {
194                 Log.Info(Globals.LogTag, "Getting getting Mock");
195                 return _isEnableMock;
196             }
197             set
198             {
199                 _isEnableMock = value;
200                 SetEnableMock();
201             }
202         }
203
204         internal IntPtr GetHandle()
205         {
206             return _handle;
207         }
208
209         private void SetEnableMock()
210         {
211             int ret = Interop.Locator.EnableMock(_isEnableMock);
212             if (((LocationError)ret != LocationError.None))
213             {
214                 Log.Error(Globals.LogTag, "Error Set Enable Mock Type," + (LocationError)ret);
215                 LocationErrorFactory.ThrowLocationException(ret);
216             }
217         }
218
219         /// <summary>
220         /// Starts the Location Manager which has been created using the specified method.
221         /// </summary>
222         public void Start()
223         {
224             Log.Info(Globals.LogTag, "Starting Location Manager");
225             if (Locator.s_locatorReference == null)
226             {
227                 int ret = Interop.Locator.Start(_handle);
228                 if (((LocationError)ret != LocationError.None))
229                 {
230                     Log.Error(Globals.LogTag, "Error Starting Location Manager," + (LocationError)ret);
231                     LocationErrorFactory.ThrowLocationException(ret);
232                 }
233                 Locator.s_locatorReference = this;
234                 _isStarted = true;
235                 Log.Info(Globals.LogTag, "Locator reference set");
236             }
237             else
238             {
239                 Log.Error(Globals.LogTag, "Error, previous instance of Locator should be stopped before starting a new one," + LocationError.NotSupported);
240                 LocationErrorFactory.ThrowLocationException((int)LocationError.NotSupported);
241             }
242         }
243
244         /// <summary>
245         /// Stops the Location Manager which has been activated using the specified method.
246         /// Does not destroy the manager.
247         /// </summary>
248         public void Stop()
249         {
250             Log.Info(Globals.LogTag, "Stopping Location Manager");
251             int ret = Interop.Locator.Stop(_handle);
252             if (((LocationError)ret != LocationError.None))
253             {
254                 Log.Error(Globals.LogTag, "Error stopping Location Manager," + (LocationError)ret);
255                 LocationErrorFactory.ThrowLocationException(ret);
256             }
257             Locator.s_locatorReference = null;
258             _isStarted = false;
259        }
260
261         /// <summary>
262         /// Sets a mock location for the given location method.
263         /// </summary>
264         /// <param name="location"> The location object containing the mock location details.</param>
265         public void SetMockLocation(Location location)
266         {
267             Log.Info(Globals.LogTag, "Setting mock location");
268             int ret = Interop.Locator.SetMockLocation(_handle, location.Latitude, location.Longitude, location.Altitude, location.Speed, location.Direction, location.HorizontalAccuracy);
269             if (((LocationError)ret == LocationError.None))
270             {
271                 _location.Altitude = 0;
272                 _location.Latitude = location.Latitude;
273                 _location.Longitude = location.Longitude;
274                 _location.Speed = location.Speed;
275                 _location.Direction = location.Direction;
276                 _location.HorizontalAccuracy = location.HorizontalAccuracy;
277             }
278             else
279             {
280                 Log.Error(Globals.LogTag, "Error in setting up location mocking," + (LocationError)ret);
281                 LocationErrorFactory.ThrowLocationException(ret);
282             }
283         }
284
285         /// <summary>
286         /// Clears a mock location for the given location method.
287         /// </summary>
288         public void ClearMock()
289         {
290             Log.Info(Globals.LogTag, "Clear mock location");
291             int ret = Interop.Locator.ClearMock(_handle);
292             if (((LocationError)ret != LocationError.None))
293             {
294                 Log.Error(Globals.LogTag, "Error in clear up location mocking," + (LocationError)ret);
295                 LocationErrorFactory.ThrowLocationException(ret);
296             }
297         }
298
299         /// <summary>
300         /// Gets the details of the location asynchronously.
301         /// </summary>
302         /// <param name="timeout"> Timeout to stop requesting single location after(seconds).</param>
303         /// <returns> A task which contains the current location details</returns>
304         public Task<Location> GetLocationAsync(int timeout)
305         {
306             var task = new TaskCompletionSource<Location>();
307             IntPtr id = IntPtr.Zero;
308             lock (_callback_map)
309             {
310                 id = (IntPtr)_requestId++;
311                 _callback_map[id] = (LocationError error, double latitude, double longitude, double altitude, int timestamp, double speed, double direction, double climb, IntPtr userData) =>
312                 {
313                     if (error != LocationError.None)
314                     {
315                         Log.Error(Globals.LogTag, "Error in getting up location information," + (LocationError)error);
316                     }
317                     else
318                     {
319                         Log.Info(Globals.LogTag, "Creating a current location object");
320                         _location = new Location(latitude, longitude, altitude, 0.0, direction, speed, timestamp);
321                         task.SetResult(_location);
322                     }
323                     lock (_callback_map)
324                     {
325                         _callback_map.Remove(userData);
326                     }
327                 };
328             }
329
330             int ret = Interop.LocatorEvent.GetSingleLocation(_handle, timeout, _callback_map[id], id);
331             if (((LocationError)ret != LocationError.None))
332             {
333                 Log.Error(Globals.LogTag, "Error in setting up location mocking," + (LocationError)ret);
334                 LocationErrorFactory.ThrowLocationException(ret);
335             }
336             return task.Task;
337         }
338
339
340         /// <summary>
341         /// Gets the details of the location.
342         /// </summary>
343         /// <returns> which contains the current location details</returns>
344         public Location GetLocation()
345         {
346             double altitude = 0;
347             double latitude = 0;
348             double longitude = 0;
349             double climb = 0;
350             double direction = 0;
351             double speed = 0;
352             LocationAccuracy level = LocationAccuracy.None;
353             double horizontal = 0;
354             double vertical = 0;
355             int timestamp = 0;
356
357             if (_isStarted)
358             {
359                 Log.Info(Globals.LogTag, "Get current location information");
360                 int ret = Interop.Locator.GetLocation(_handle, out altitude, out latitude, out longitude, out climb, out direction, out speed, out level, out horizontal, out vertical, out timestamp);
361                 if (((LocationError)ret != LocationError.None))
362                 {
363                     Log.Error(Globals.LogTag, "Error in get current location infomation," + (LocationError)ret);
364                     LocationErrorFactory.ThrowLocationException(ret);
365                 }
366             }
367             else
368             {
369                 Log.Info(Globals.LogTag, "Get last location information");
370                 int ret = Interop.Locator.GetLastLocation(_handle, out altitude, out latitude, out longitude, out climb, out direction, out speed, out level, out horizontal, out vertical, out timestamp);
371                 if (((LocationError)ret != LocationError.None))
372                 {
373                     Log.Error(Globals.LogTag, "Error in get last location information," + (LocationError)ret);
374                     LocationErrorFactory.ThrowLocationException(ret);
375                 }
376             }
377
378             Location location = new Location(latitude, longitude, altitude, horizontal, direction, speed, timestamp);
379             _location = location;
380
381             return location;
382         }
383
384
385         /// <summary>
386         /// Adds a bounds for a given locator.
387         /// </summary>
388         /// <param name="locationBoundary"> The boundary object to be added to the locator.</param>
389         public void AddBoundary(LocationBoundary locationBoundary)
390         {
391             Log.Info(Globals.LogTag, "AddBoundary called");
392
393             int ret = Interop.Locator.AddBoundary(_handle, locationBoundary.GetHandle());
394             if ((LocationBoundError)ret != LocationBoundError.None)
395             {
396                 Log.Error(Globals.LogTag, "Error Adding Boundary," + (LocationBoundError)ret);
397                 LocationErrorFactory.ThrowLocationBoundaryException(ret);
398             }
399         }
400
401         /// <summary>
402         /// Deletes a bounds for a given locator.
403         /// </summary>
404         /// <param name="locationBoundary"> The boundary object to be removed from the locator.</param>
405         public void RemoveBoundary(LocationBoundary locationBoundary)
406         {
407             Log.Info(Globals.LogTag, "RemoveBoundary called");
408             int ret = Interop.Locator.RemoveBoundary(_handle, locationBoundary.GetHandle());
409             if ((LocationBoundError)ret != LocationBoundError.None)
410             {
411                 Log.Error(Globals.LogTag, "Error Removing Boundary," + (LocationBoundError)ret);
412                 LocationErrorFactory.ThrowLocationBoundaryException(ret);
413             }
414         }
415
416         /// <summary>
417         /// The overidden Dispose method of the IDisposable class.
418         /// </summary>
419         public void Dispose()
420         {
421             Dispose(true);
422             GC.SuppressFinalize(this);
423         }
424
425         protected virtual void Dispose(bool disposing)
426         {
427             if (_disposed)
428                 return;
429
430             DestroyHandle();
431             _disposed = true;
432         }
433
434         private void DestroyHandle()
435         {
436             int ret = Interop.Locator.Destroy(_handle);
437             if (((LocationError)ret != LocationError.None))
438             {
439                 Log.Error(Globals.LogTag, "Error in Destroy handle" + (LocationError)ret);
440                 LocationErrorFactory.ThrowLocationException(ret);
441             }
442         }
443
444         /// <summary>
445         /// (event) ServiceStateChanged Event is invoked when the location service state is changed.
446         /// </summary>
447         public event EventHandler<ServiceStateChangedEventArgs> ServiceStateChanged
448         {
449             add
450             {
451                 Log.Info(Globals.LogTag, "ServiceStateChanged called");
452                 if (_serviceStateChanged == null)
453                 {
454                     Log.Info(Globals.LogTag, "Calling function SetServiceStateChangedCallback");
455                     SetServiceStateChangedCallback();
456                 }
457                 _serviceStateChanged += value;
458             }
459             remove
460             {
461                 Log.Info(Globals.LogTag, "Callback removed");
462                 _serviceStateChanged -= value;
463
464                 if (_serviceStateChanged == null)
465                 {
466                     Log.Info(Globals.LogTag, "Calling function UnSetServiceStateChangedCallback");
467                     UnSetServiceStateChangedCallback();
468                 }
469             }
470         }
471
472         private void SetServiceStateChangedCallback()
473         {
474             Log.Info(Globals.LogTag, "Calling Interop.LocatorEvent.SetServiceStateChangedCallback");
475             int ret = Interop.LocatorEvent.SetServiceStateChangedCallback(_handle, ServiceStateChangedCallback, IntPtr.Zero);
476             if (((LocationError)ret != LocationError.None))
477             {
478                 Log.Error(Globals.LogTag, "Error in Setting Service State Changed Callback," + (LocationError)ret);
479                 LocationErrorFactory.ThrowLocationException(ret);
480             }
481         }
482
483         private void UnSetServiceStateChangedCallback()
484         {
485             Log.Info(Globals.LogTag, "Calling Interop.LocatorEvent.UnSetServiceStateChangedCallback");
486             int ret = Interop.LocatorEvent.UnSetServiceStateChangedCallback(_handle);
487             if (((LocationError)ret != LocationError.None))
488             {
489                 Log.Error(Globals.LogTag, "Error in UnSetting Service State Changed Callback," + (LocationError)ret);
490                 LocationErrorFactory.ThrowLocationException(ret);
491             }
492         }
493
494         private void ServiceStateChangedCallback(ServiceState state, IntPtr userData)
495         {
496             Log.Info(Globals.LogTag, "Inside ServiceStateChangedCallback");
497             _serviceStateChanged?.Invoke(this, new ServiceStateChangedEventArgs(state));
498         }
499
500         /// <summary>
501         /// (event) ZoneChanged is  invoked when the previously set boundary area is entered or left.
502         /// </summary>
503         public event EventHandler<ZoneChangedEventArgs> ZoneChanged
504         {
505             add
506             {
507                 Log.Info(Globals.LogTag, "ZoneChanged called");
508                 if (_zoneChanged == null)
509                 {
510                     Log.Info(Globals.LogTag, "Calling function SetZoneChangedCallback");
511                     SetZoneChangedCallback();
512                 }
513                 _zoneChanged += value;
514             }
515             remove
516             {
517                 Log.Info(Globals.LogTag, "Callback removed");
518                 _zoneChanged -= value;
519
520                 if (_zoneChanged == null)
521                 {
522                     Log.Info(Globals.LogTag, "Calling function UnSetZoneChangedCallback");
523                     UnSetZoneChangedCallback();
524                 }
525             }
526         }
527
528         private void SetZoneChangedCallback()
529         {
530             Log.Info(Globals.LogTag, "Inside SetZoneChangedCallback");
531             int ret = Interop.LocatorEvent.SetZoneChangedCallback(_handle, ZoneChangedCallback, IntPtr.Zero);
532             if (((LocationError)ret != LocationError.None))
533             {
534                 Log.Error(Globals.LogTag, "Error in Setting Zone Changed Callback," + (LocationError)ret);
535                 LocationErrorFactory.ThrowLocationException(ret);
536             }
537         }
538
539         private void UnSetZoneChangedCallback()
540         {
541             Log.Info(Globals.LogTag, "Inside UnSetZoneChangedCallback");
542             int ret = Interop.LocatorEvent.UnSetZoneChangedCallback(_handle);
543             if (((LocationError)ret != LocationError.None))
544             {
545                 Log.Error(Globals.LogTag, "Error in UnSetting Zone Changed Callback," + (LocationError)ret);
546             }
547         }
548
549         private void ZoneChangedCallback(BoundaryState state, double latitude, double longitude, double altitude, int timestamp, IntPtr userData)
550         {
551             Log.Info(Globals.LogTag, "Inside ZoneChangedCallback");
552             DateTime timeStamp = DateTime.Now;
553             if (timestamp != 0)
554             {
555                 DateTime start = DateTime.SpecifyKind(new DateTime(1970, 1, 1).AddSeconds(timestamp), DateTimeKind.Utc);
556                 timeStamp = start.ToLocalTime();
557             }
558             _zoneChanged?.Invoke(this, new ZoneChangedEventArgs(state, latitude, longitude, altitude, timeStamp));
559         }
560
561         /// <summary>
562         /// (event) SetttingChanged is raised when the location setting is changed.
563         /// </summary>
564         public event EventHandler<SettingChangedEventArgs> SettingChanged
565         {
566             add
567             {
568                 Log.Info(Globals.LogTag, "Adding SettingChanged EventHandler");
569                 if (_settingChanged == null)
570                 {
571                     Log.Info(Globals.LogTag, "Calling function SetSettingChangedCallback");
572                     SetSettingChangedCallback();
573                 }
574                 _settingChanged += value;
575             }
576             remove
577             {
578                 Log.Info(Globals.LogTag, "Removing SettingChanged EventHandler");
579                 _settingChanged -= value;
580
581                 if (_settingChanged == null)
582                 {
583                     Log.Info(Globals.LogTag, "Calling function UnSetSettingChangedCallback");
584                     UnSetSettingChangedCallback();
585                 }
586             }
587         }
588
589         private void SetSettingChangedCallback()
590         {
591             Log.Info(Globals.LogTag, "Calling SetSettingChangedCallback");
592             int ret = Interop.LocatorEvent.SetSettingChangedCallback((int)_locationType, SettingChangedCallback, IntPtr.Zero);
593             if (((LocationError)ret != LocationError.None))
594             {
595                 Log.Error(Globals.LogTag, "Error in Setting Changed Callback," + (LocationError)ret);
596                 LocationErrorFactory.ThrowLocationException(ret);
597             }
598         }
599
600         private void UnSetSettingChangedCallback()
601         {
602             Log.Info(Globals.LogTag, "Calling UnSetSettingChangedCallback");
603             int ret = Interop.LocatorEvent.UnSetSettingChangedCallback((int)_locationType);
604             if (((LocationError)ret != LocationError.None))
605             {
606                 Log.Error(Globals.LogTag, "Error in Unsetting Setting's Callback," + (LocationError)ret);
607                 LocationErrorFactory.ThrowLocationException(ret);
608             }
609         }
610
611         private void SettingChangedCallback(LocationType method, bool enable, IntPtr userData)
612         {
613             Log.Info(Globals.LogTag, "Calling SettingChangedCallback");
614             _settingChanged?.Invoke(this, new SettingChangedEventArgs(method, enable));
615         }
616
617         /// <summary>
618         /// (event) DistanceBasedLocationChanged is raised with updated location information.
619         /// The callback will be invoked at minimum interval or minimum distance with updated position information.
620         /// </summary>
621         public event EventHandler<LocationChangedEventArgs> DistanceBasedLocationChanged
622         {
623             add
624             {
625                 Log.Info(Globals.LogTag, "Adding DistanceBasedLocationChanged EventHandler");
626                 if (_distanceBasedLocationChanged == null)
627                 {
628                     Log.Info(Globals.LogTag, "Calling function SetDistanceBasedLocationChangedCallback");
629                     SetDistanceBasedLocationChangedCallback();
630                 }
631                 _distanceBasedLocationChanged += value;
632             }
633             remove
634             {
635                 Log.Info(Globals.LogTag, "Removing DistanceBasedLocationChanged EventHandler");
636                 _distanceBasedLocationChanged -= value;
637
638                 if (_distanceBasedLocationChanged == null)
639                 {
640                     Log.Info(Globals.LogTag, "Calling function UnSetDistanceBasedLocationChangedCallback");
641                     UnSetDistanceBasedLocationChangedCallback();
642                 }
643             }
644         }
645
646         private void SetDistanceBasedLocationChangedCallback()
647         {
648             Log.Info(Globals.LogTag, "SetDistanceBasedLocationChangedCallback");
649             int ret = Interop.LocatorEvent.SetDistanceBasedLocationChangedCallback(_handle, DistanceBasedLocationChangedCallback, _stayInterval, _distance, IntPtr.Zero);
650             if (((LocationError)ret != LocationError.None))
651             {
652                 Log.Error(Globals.LogTag, "Error in Setting Distance based location changed Callback," + (LocationError)ret);
653                 LocationErrorFactory.ThrowLocationException(ret);
654             }
655         }
656
657         private void UnSetDistanceBasedLocationChangedCallback()
658         {
659             Log.Info(Globals.LogTag, "UnSetDistanceBasedLocationChangedCallback");
660             int ret = Interop.LocatorEvent.UnSetDistanceBasedLocationChangedCallback(_handle);
661             if (((LocationError)ret != LocationError.None))
662             {
663                 Log.Error(Globals.LogTag, "Error in UnSetting Distance based location changed Callback," + (LocationError)ret);
664                 LocationErrorFactory.ThrowLocationException(ret);
665             }
666         }
667
668         private void DistanceBasedLocationChangedCallback(double latitude, double longitude, double altitude, double speed, double direction, double horizontalAccuracy, int timestamp, IntPtr userData)
669         {
670             Log.Info(Globals.LogTag, "DistanceBasedLocationChangedCallback");
671             Location location = new Location(latitude, longitude, altitude, horizontalAccuracy, direction, speed, timestamp);
672             _distanceBasedLocationChanged?.Invoke(this, new LocationChangedEventArgs(location));
673         }
674
675         /// <summary>
676         /// (event)LocationUpdated is raised at defined intervals of time with updated location information.
677         /// The callback will be invoked periodically.
678         /// </summary>
679         public event EventHandler<LocationChangedEventArgs> LocationChanged
680         {
681             add
682             {
683                 Log.Info(Globals.LogTag, "Adding LocationChanged EventHandler");
684                 if (_locationChanged == null)
685                 {
686                     Log.Info(Globals.LogTag, "Calling function SetLocationChangedCallback");
687                     SetLocationChangedCallback();
688                 }
689                 _locationChanged += value;
690             }
691             remove
692             {
693                 Log.Info(Globals.LogTag, "Adding LocationChanged EventHandler");
694                 _locationChanged -= value;
695
696                 if (_locationChanged == null)
697                 {
698                     Log.Info(Globals.LogTag, "calling function UnSetLocationChangedCallback");
699                     UnSetLocationChangedCallback();
700                 }
701             }
702         }
703
704         private void SetLocationChangedCallback()
705         {
706             Log.Info(Globals.LogTag, "Calling SetLocationChangedCallback");
707             int ret = Interop.LocatorEvent.SetLocationChangedCallback(_handle, LocationChangedCallback, _interval, IntPtr.Zero);
708             if (((LocationError)ret != LocationError.None))
709             {
710                 Log.Error(Globals.LogTag, "Error in Setting location changed Callback," + (LocationError)ret);
711                 LocationErrorFactory.ThrowLocationException(ret);
712             }
713         }
714
715         private void UnSetLocationChangedCallback()
716         {
717             Log.Info(Globals.LogTag, "Calling UnSetLocationChangedCallback");
718             int ret = Interop.LocatorEvent.UnSetLocationChangedCallback(_handle);
719             if (((LocationError)ret != LocationError.None))
720             {
721                 Log.Error(Globals.LogTag, "Error in UnSetting location changed Callback," + (LocationError)ret);
722                 LocationErrorFactory.ThrowLocationException(ret);
723             }
724         }
725
726         private void LocationChangedCallback(double latitude, double longitude, double altitude, double speed, double direction, double horizontalAccuracy, int timestamp, IntPtr userData)
727         {
728             Log.Info(Globals.LogTag, "LocationChangedCallback has been called");
729             Location location = new Location(latitude, longitude, altitude, horizontalAccuracy, direction, speed, timestamp);
730             _location = location;
731             _locationChanged?.Invoke(this, new LocationChangedEventArgs(location));
732         }
733     }
734 }