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