Release 4.0.0-preview1-00172
[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     /// This class contains the functionality for obtaining the geographical information and setting the 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     /// <since_tizen> 3 </since_tizen>
36     public class Locator : IDisposable
37     {
38         private int _interval = 1;
39         private int _stayInterval = 120;
40         private int _batchInterval = 0;
41         private int _batchPeriod = 0;
42         private int _requestId = 0;
43         private double _distance = 120.0;
44         private bool _isEnableMock = false;
45         private bool _disposed = false;
46         private bool _isStarted = false;
47         private IntPtr _handle;
48         private LocationType _locationType;
49         private Location _location = null;
50         private Dictionary<IntPtr, Interop.LocatorEvent.LocationUpdatedCallback> _callback_map = new Dictionary<IntPtr, Interop.LocatorEvent.LocationUpdatedCallback>();
51
52         private Interop.LocatorEvent.ServiceStatechangedCallback _serviceStateChangedCallback;
53         private Interop.LocatorEvent.ZonechangedCallback _zoneChangedCallback;
54         private Interop.LocatorEvent.SettingchangedCallback _settingChangedCallback;
55         private Interop.LocatorEvent.LocationchangedCallback _distanceBasedLocationChangedCallback;
56         private Interop.LocatorEvent.LocationchangedCallback _locationChangedCallback;
57         private Interop.LocatorEvent.LocationBatchCallback _locationBatchCallback;
58         private Interop.LocatorEvent.LocationBatchGetCallback _locationBatchGetCallback;
59
60         private EventHandler<ZoneChangedEventArgs> _zoneChanged;
61         private EventHandler<ServiceStateChangedEventArgs> _serviceStateChanged;
62         private EventHandler<SettingChangedEventArgs> _settingChanged;
63         private EventHandler<LocationChangedEventArgs> _distanceBasedLocationChanged;
64         private EventHandler<LocationChangedEventArgs> _locationChanged;
65
66         /// <summary>
67         /// The constructor of the Locator class.
68         /// </summary>
69         /// <since_tizen> 3 </since_tizen>
70         /// <param name="locationType"> The back-end positioning method to be used for LBS.</param>
71         /// <feature>http://tizen.org/feature/location.gps</feature>
72         /// <feature>http://tizen.org/feature/location.wps</feature>
73         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
74         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
75         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
76         public Locator(LocationType locationType)
77         {
78             Log.Info(Globals.LogTag, "Locator Constructor");
79             int ret = Interop.Locator.Create((int)locationType, out _handle);
80             if (((LocationError)ret != LocationError.None))
81             {
82                 Log.Error(Globals.LogTag, "Error creating Location Manager," + (LocationError)ret);
83                 throw LocationErrorFactory.ThrowLocationException(ret);
84             }
85             _location = new Location();
86             _locationType = locationType;
87         }
88
89         /// <summary>
90         /// The destructor of the Locator class.
91         /// </summary>
92         /// <since_tizen> 3 </since_tizen>
93         ~Locator()
94         {
95             Dispose(false);
96         }
97
98         /// <summary>
99         /// The time interval between callback updates.
100         /// Should be in the range of 1~120 seconds.
101         /// </summary>
102         /// <since_tizen> 3 </since_tizen>
103         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
104         public int Interval
105         {
106             get
107             {
108                 Log.Info(Globals.LogTag, "Getting the Callback Interval");
109                 return _interval;
110             }
111             set
112             {
113                 Log.Info(Globals.LogTag, "Setting the Callback Interval");
114                 if (value > 0 && value <= 120)
115                 {
116                     _interval = value;
117                 }
118                 else
119                 {
120                     Log.Error(Globals.LogTag, "Error setting Callback Interval");
121                     throw LocationErrorFactory.ThrowLocationException((int)LocationError.InvalidParameter);
122                 }
123             }
124         }
125
126         /// <summary>
127         /// The time interval between the distance-based location callback updates.
128         /// Should be in the range of 1~120 seconds.
129         /// </summary>
130         /// <since_tizen> 3 </since_tizen>
131         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
132         public int StayInterval
133         {
134             get
135             {
136                 Log.Info(Globals.LogTag, "Getting the StayInterval");
137                 return _stayInterval;
138             }
139             set
140             {
141                 Log.Info(Globals.LogTag, "Setting the StayInterval");
142                 if (value > 0 && value <= 120)
143                 {
144                     _stayInterval = value;
145                 }
146                 else
147                 {
148                     Log.Error(Globals.LogTag, "Error Setting the StayInterval");
149                     throw LocationErrorFactory.ThrowLocationException((int)LocationError.InvalidParameter);
150                 }
151             }
152         }
153
154         /// <summary>
155         /// The time interval between the position collection in batch mode.
156         /// Should be in the range of 1~255 seconds.
157         /// </summary>
158         /// <since_tizen> 3 </since_tizen>
159         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
160         public int BatchInterval
161         {
162             get
163             {
164                 Log.Info(Globals.LogTag, "Getting the Batch Interval");
165                 return _batchInterval;
166             }
167             set
168             {
169                 Log.Info(Globals.LogTag, "Setting the Batch Interval");
170                 if (value > 0 && value <= 255)
171                 {
172                     _batchInterval = value;
173                 }
174                 else
175                 {
176                     Log.Error(Globals.LogTag, "Error setting Callback Interval");
177                     throw LocationErrorFactory.ThrowLocationException((int)LocationError.InvalidParameter);
178                 }
179             }
180         }
181
182         /// <summary>
183         /// The time interval between batch callback updates. The BatchPeriod should be greater than or equal to the BatchInterval. If the BatchPeriod is zero or smaller than the BatchInterval, then the batch mode will not work. In addition, sometimes the period may not work as you intended, the maximum permissible value for the batch period is device specific.
184         /// Should be in the range of 0~60000 seconds.
185         /// </summary>
186         /// <since_tizen> 3 </since_tizen>
187         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
188         public int BatchPeriod
189         {
190             get
191             {
192                 Log.Info(Globals.LogTag, "Getting the Batch Period");
193                 return _batchPeriod;
194             }
195             set
196             {
197                 Log.Info(Globals.LogTag, "Setting the Batch Period");
198                 if (value >= 0 && value <= 60000)
199                 {
200                     _batchPeriod = value;
201                 }
202                 else
203                 {
204                     Log.Error(Globals.LogTag, "Error setting Batch Period");
205                     throw LocationErrorFactory.ThrowLocationException((int)LocationError.InvalidParameter);
206                 }
207             }
208         }
209
210         /// <summary>
211         /// The distance between callback updates.
212         /// Should be in the range of 1-120 meters.
213         /// </summary>
214         /// <since_tizen> 3 </since_tizen>
215         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
216         public double Distance
217         {
218             get
219             {
220                 Log.Info(Globals.LogTag, "Getting the Distance Interval");
221                 return _distance;
222             }
223             set
224             {
225                 Log.Info(Globals.LogTag, "Setting the Distance Interval");
226                 if (value > 0 && value <= 120)
227                 {
228                     _distance = value;
229                 }
230                 else
231                 {
232                     Log.Error(Globals.LogTag, "Error Setting the Distance");
233                     throw LocationErrorFactory.ThrowLocationException((int)LocationError.InvalidParameter);
234                 }
235             }
236         }
237
238         /// <summary>
239         /// Gets the location object.
240         /// </summary>
241         /// <since_tizen> 3 </since_tizen>
242         public Location Location
243         {
244             get
245             {
246                 Log.Info(Globals.LogTag, "Getting location details");
247                 return _location;
248            }
249         }
250
251         /// <summary>
252         /// Gets the type used to obtain the location data.
253         /// </summary>
254         /// <since_tizen> 3 </since_tizen>
255         public LocationType LocationType
256         {
257             get
258             {
259                 Log.Info(Globals.LogTag, "Getting LocationType");
260                 return _locationType;
261             }
262         }
263
264         /// <summary>
265         /// Gets the status whether the mock location is enabled or not.
266         /// </summary>
267         /// <since_tizen> 3 </since_tizen>
268         /// <privilege>http://tizen.org/privilege/location</privilege>
269         /// <exception cref="UnauthroizedAccessException">Thrown when the application has no privilege to use the location.</exception>
270         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
271         public bool EnableMock
272         {
273             get
274             {
275                 Log.Info(Globals.LogTag, "Getting getting Mock");
276                 _isEnableMock = GetEnableMock();
277                 return _isEnableMock;
278             }
279             set
280             {
281                 _isEnableMock = value;
282                 SetEnableMock();
283             }
284         }
285
286         internal IntPtr GetHandle()
287         {
288             return _handle;
289         }
290
291         private bool GetEnableMock()
292         {
293             bool status = false;
294             int ret = Interop.Locator.IsEnabledMock(out status);
295             if (((LocationError)ret != LocationError.None))
296             {
297                 Log.Error(Globals.LogTag, "Error Get Enable Mock Status," + (LocationError)ret);
298                 throw LocationErrorFactory.ThrowLocationException(ret);
299             }
300             return status;
301         }
302
303         private void SetEnableMock()
304         {
305             int ret = Interop.Locator.EnableMock(_isEnableMock);
306             if (((LocationError)ret != LocationError.None))
307             {
308                 Log.Error(Globals.LogTag, "Error Set Enable Mock Status," + (LocationError)ret);
309                 throw LocationErrorFactory.ThrowLocationException(ret);
310             }
311         }
312
313         /// <summary>
314         /// Starts the Location Manager which has been created using the specified method.
315         /// </summary>
316         /// <since_tizen> 3 </since_tizen>
317         /// <privilege>http://tizen.org/privilege/location</privilege>
318         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
319         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
320         /// <exception cref="UnauthroizedAccessException">Thrown when the application has no privilege to use the location.</exception>
321         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
322         public void Start()
323         {
324             Log.Info(Globals.LogTag, "Starting Location Manager");
325             if (_batchPeriod > 0 && _batchPeriod > _batchInterval)
326             {
327                 int ret = Interop.Locator.StartBatch(_handle);
328                 if (((LocationError)ret != LocationError.None))
329                 {
330                     Log.Error(Globals.LogTag, "Error Starting Location Batch mode," + (LocationError)ret);
331                     throw LocationErrorFactory.ThrowLocationException(ret);
332                 }
333             }
334             else
335             {
336                 int ret = Interop.Locator.Start(_handle);
337                    if (((LocationError)ret != LocationError.None))
338                 {
339                     Log.Error(Globals.LogTag, "Error Starting Location Manager," + (LocationError)ret);
340                     throw LocationErrorFactory.ThrowLocationException(ret);
341                 }
342             }
343             _isStarted = true;
344         }
345
346         /// <summary>
347         /// Stops the Location Manager which has been activated using the specified method.
348         /// Does not destroy the manager.
349         /// </summary>
350         /// <since_tizen> 3 </since_tizen>
351         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
352         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
353         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
354         public void Stop()
355         {
356             Log.Info(Globals.LogTag, "Stopping Location Manager");
357             int ret = Interop.Locator.Stop(_handle);
358             if (((LocationError)ret != LocationError.None))
359             {
360                 Log.Error(Globals.LogTag, "Error stopping Location Manager," + (LocationError)ret);
361                 throw LocationErrorFactory.ThrowLocationException(ret);
362             }
363             _isStarted = false;
364        }
365
366         /// <summary>
367         /// Sets a mock location for the given location method.
368         /// </summary>
369         /// <since_tizen> 3 </since_tizen>
370         /// <param name="location"> The location object containing the mock location details.</param>
371         /// <privilege>http://tizen.org/privilege/location</privilege>
372         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
373         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
374         /// <exception cref="UnauthroizedAccessException">Thrown when the application has no privilege to use the location.</exception>
375         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
376         public void SetMockLocation(Location location)
377         {
378             Log.Info(Globals.LogTag, "Setting mock location");
379             int ret = Interop.Locator.SetMockLocation(_handle, location.Latitude, location.Longitude, location.Altitude, location.Speed, location.Direction, location.Accuracy);
380             if (((LocationError)ret == LocationError.None))
381             {
382                 _location.Latitude = location.Latitude;
383                 _location.Longitude = location.Longitude;
384                 _location.Altitude = location.Altitude;
385                 _location.Speed = location.Speed;
386                 _location.Direction = location.Direction;
387                 _location.Accuracy = location.Accuracy;
388             }
389             else
390             {
391                 Log.Error(Globals.LogTag, "Error in setting up location mocking," + (LocationError)ret);
392                 throw LocationErrorFactory.ThrowLocationException(ret);
393             }
394         }
395
396         /// <summary>
397         /// Clears a mock location for the given location method.
398         /// </summary>
399         /// <since_tizen> 3 </since_tizen>
400         /// <privilege>http://tizen.org/privilege/location</privilege>
401         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
402         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
403         /// <exception cref="UnauthroizedAccessException">Thrown when the application has no privilege to use the location.</exception>
404         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
405         public void ClearMock()
406         {
407             Log.Info(Globals.LogTag, "Clear mock location");
408             int ret = Interop.Locator.ClearMock(_handle);
409             if (((LocationError)ret != LocationError.None))
410             {
411                 Log.Error(Globals.LogTag, "Error in clear up location mocking," + (LocationError)ret);
412                 throw LocationErrorFactory.ThrowLocationException(ret);
413             }
414         }
415
416         /// <summary>
417         /// Gets the details of the location asynchronously.
418         /// </summary>
419         /// <since_tizen> 3 </since_tizen>
420         /// <param name="timeout"> Timeout to stop requesting a single location after (seconds).</param>
421         /// <returns> A task which contains the current location details.</returns>
422         /// <privilege>http://tizen.org/privilege/location</privilege>
423         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
424         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
425         /// <exception cref="UnauthroizedAccessException">Thrown when the application has no privilege to use the location.</exception>
426         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
427         public Task<Location> GetLocationAsync(int timeout)
428         {
429             var task = new TaskCompletionSource<Location>();
430             IntPtr id = IntPtr.Zero;
431             lock (_callback_map)
432             {
433                 id = (IntPtr)_requestId++;
434                 _callback_map[id] = (LocationError error, double latitude, double longitude, double altitude, int timestamp, double speed, double direction, double climb, IntPtr userData) =>
435                 {
436                     if (error != LocationError.None)
437                     {
438                         Log.Error(Globals.LogTag, "Error in getting up location information," + (LocationError)error);
439                     }
440                     else
441                     {
442                         Log.Info(Globals.LogTag, "Creating a current location object");
443                         _location = new Location(latitude, longitude, altitude, speed, direction, 0.0, timestamp);
444                         task.SetResult(_location);
445                     }
446                     lock (_callback_map)
447                     {
448                         _callback_map.Remove(userData);
449                     }
450                 };
451             }
452
453             int ret = Interop.LocatorEvent.GetSingleLocation(_handle, timeout, _callback_map[id], id);
454             if (((LocationError)ret != LocationError.None))
455             {
456                 Log.Error(Globals.LogTag, "Error in setting up location mocking," + (LocationError)ret);
457                 throw LocationErrorFactory.ThrowLocationException(ret);
458             }
459             return task.Task;
460         }
461
462
463         /// <summary>
464         /// Gets the details of the location.
465         /// </summary>
466         /// <since_tizen> 3 </since_tizen>
467         /// <returns> Which contains the current location details.</returns>
468         /// <privilege>http://tizen.org/privilege/location</privilege>
469         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
470         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
471         /// <exception cref="UnauthroizedAccessException">Thrown when the application has no privilege to use the location.</exception>
472         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
473         public Location GetLocation()
474         {
475             double latitude = 0;
476             double longitude = 0;
477             double altitude = 0;
478             double climb = 0;
479             double speed = 0;
480             double direction = 0;
481             int level = 0;
482             double accuracy = 0;
483             double vertical = 0;
484             int timestamp = 0;
485
486             if (_isStarted)
487             {
488                 Log.Info(Globals.LogTag, "Get current location information");
489                 int ret = Interop.Locator.GetLocation(_handle, out altitude, out latitude, out longitude, out climb, out direction, out speed, out level, out accuracy, out vertical, out timestamp);
490                 if (((LocationError)ret != LocationError.None))
491                 {
492                     Log.Error(Globals.LogTag, "Error in get current location information," + (LocationError)ret);
493                     throw LocationErrorFactory.ThrowLocationException(ret);
494                 }
495             }
496             else
497             {
498                 Log.Info(Globals.LogTag, "Get last location information");
499                 int ret = Interop.Locator.GetLastLocation(_handle, out altitude, out latitude, out longitude, out climb, out direction, out speed, out level, out accuracy, out vertical, out timestamp);
500                 if (((LocationError)ret != LocationError.None))
501                 {
502                     Log.Error(Globals.LogTag, "Error in get last location information," + (LocationError)ret);
503                     throw LocationErrorFactory.ThrowLocationException(ret);
504                 }
505             }
506
507             Location location = new Location(latitude, longitude, altitude, speed, direction, accuracy, timestamp);
508             _location = location;
509
510             return location;
511         }
512
513
514         /// <summary>
515         /// Adds a bound for a given locator.
516         /// </summary>
517         /// <since_tizen> 3 </since_tizen>
518         /// <param name="locationBoundary">The boundary object to be added to the locator.</param>
519         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
520         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
521         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
522         public void AddBoundary(LocationBoundary locationBoundary)
523         {
524             Log.Info(Globals.LogTag, "AddBoundary called");
525
526             int ret = Interop.Locator.AddBoundary(_handle, locationBoundary.GetHandle());
527             if ((LocationBoundError)ret != LocationBoundError.None)
528             {
529                 Log.Error(Globals.LogTag, "Error Adding Boundary," + (LocationBoundError)ret);
530                 throw LocationErrorFactory.ThrowLocationBoundaryException(ret);
531             }
532         }
533
534         /// <summary>
535         /// Deletes a bound for a given locator.
536         /// </summary>
537         /// <since_tizen> 3 </since_tizen>
538         /// <param name="locationBoundary"> The boundary object to be removed from the locator.</param>
539         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
540         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
541         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
542         public void RemoveBoundary(LocationBoundary locationBoundary)
543         {
544             Log.Info(Globals.LogTag, "RemoveBoundary called");
545             int ret = Interop.Locator.RemoveBoundary(_handle, locationBoundary.GetHandle());
546             if ((LocationBoundError)ret != LocationBoundError.None)
547             {
548                 Log.Error(Globals.LogTag, "Error Removing Boundary," + (LocationBoundError)ret);
549                 throw LocationErrorFactory.ThrowLocationBoundaryException(ret);
550             }
551         }
552
553         /// <summary>
554         /// The overidden Dispose method of the IDisposable class.
555         /// </summary>
556         /// <since_tizen> 3 </since_tizen>
557         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
558         public void Dispose()
559         {
560             Dispose(true);
561             GC.SuppressFinalize(this);
562         }
563
564         protected virtual void Dispose(bool disposing)
565         {
566             if (_disposed)
567                 return;
568
569             if (disposing)
570                 DestroyHandle();
571
572             _disposed = true;
573         }
574
575         private void DestroyHandle()
576         {
577             int ret = Interop.Locator.Destroy(_handle);
578             if (((LocationError)ret != LocationError.None))
579             {
580                 Log.Error(Globals.LogTag, "Error in Destroy handle, " + (LocationError)ret);
581                 throw LocationErrorFactory.ThrowLocationException(ret);
582             }
583         }
584
585         /// <summary>
586         /// The ServiceStateChanged event is invoked when the location service state is changed.
587         /// </summary>
588         /// <since_tizen> 3 </since_tizen>
589         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
590         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
591         public event EventHandler<ServiceStateChangedEventArgs> ServiceStateChanged
592         {
593             add
594             {
595                 Log.Info(Globals.LogTag, "ServiceStateChanged called");
596                 if (_serviceStateChanged == null)
597                 {
598                     Log.Info(Globals.LogTag, "Calling function SetServiceStateChangedCallback");
599                     SetServiceStateChangedCallback();
600                 }
601                 _serviceStateChanged += value;
602             }
603             remove
604             {
605                 Log.Info(Globals.LogTag, "Callback removed");
606                 _serviceStateChanged -= value;
607
608                 if (_serviceStateChanged == null)
609                 {
610                     Log.Info(Globals.LogTag, "Calling function UnSetServiceStateChangedCallback");
611                     UnSetServiceStateChangedCallback();
612                 }
613             }
614         }
615
616         private void SetServiceStateChangedCallback()
617         {
618             Log.Info(Globals.LogTag, "Calling Interop.LocatorEvent.SetServiceStateChangedCallback");
619             if (_serviceStateChangedCallback == null)
620             {
621                 _serviceStateChangedCallback = (state, userData) =>
622                 {
623                     Log.Info(Globals.LogTag, "Inside ServiceStateChangedCallback");
624                     _serviceStateChanged?.Invoke(this, new ServiceStateChangedEventArgs(state));
625                 };
626             }
627
628             int ret = Interop.LocatorEvent.SetServiceStateChangedCallback(_handle, _serviceStateChangedCallback, IntPtr.Zero);
629             if (((LocationError)ret != LocationError.None))
630             {
631                 Log.Error(Globals.LogTag, "Error in Setting Service State Changed Callback," + (LocationError)ret);
632                 throw LocationErrorFactory.ThrowLocationException(ret);
633             }
634         }
635
636         private void UnSetServiceStateChangedCallback()
637         {
638             Log.Info(Globals.LogTag, "Calling Interop.LocatorEvent.UnSetServiceStateChangedCallback");
639             int ret = Interop.LocatorEvent.UnSetServiceStateChangedCallback(_handle);
640             if (((LocationError)ret != LocationError.None))
641             {
642                 Log.Error(Globals.LogTag, "Error in UnSetting Service State Changed Callback," + (LocationError)ret);
643                 throw LocationErrorFactory.ThrowLocationException(ret);
644             }
645         }
646
647         /// <summary>
648         /// The ZoneChanged event is invoked when the previously set boundary area is entered or left.
649         /// </summary>
650         /// <since_tizen> 3 </since_tizen>
651         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
652         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
653         public event EventHandler<ZoneChangedEventArgs> ZoneChanged
654         {
655             add
656             {
657                 Log.Info(Globals.LogTag, "ZoneChanged called");
658                 if (_zoneChanged == null)
659                 {
660                     Log.Info(Globals.LogTag, "Calling function SetZoneChangedCallback");
661                     SetZoneChangedCallback();
662                 }
663                 _zoneChanged += value;
664             }
665             remove
666             {
667                 Log.Info(Globals.LogTag, "Callback removed");
668                 _zoneChanged -= value;
669
670                 if (_zoneChanged == null)
671                 {
672                     Log.Info(Globals.LogTag, "Calling function UnSetZoneChangedCallback");
673                     UnSetZoneChangedCallback();
674                 }
675             }
676         }
677
678         private void SetZoneChangedCallback()
679         {
680             Log.Info(Globals.LogTag, "Inside SetZoneChangedCallback");
681             if (_zoneChangedCallback == null)
682             {
683                 _zoneChangedCallback = (state, latitude, longitude, altitude, timestamp, userData) =>
684                 {
685                     Log.Info(Globals.LogTag, "Inside ZoneChangedCallback");
686                     DateTime timeStamp = DateTime.Now;
687                     if (timestamp != 0)
688                     {
689                         DateTime start = DateTime.SpecifyKind(new DateTime(1970, 1, 1).AddSeconds(timestamp), DateTimeKind.Utc);
690                         timeStamp = start.ToLocalTime();
691                     }
692                     _zoneChanged?.Invoke(this, new ZoneChangedEventArgs(state, latitude, longitude, altitude, timeStamp));
693                 };
694             }
695
696             int ret = Interop.LocatorEvent.SetZoneChangedCallback(_handle, _zoneChangedCallback, IntPtr.Zero);
697             if (((LocationError)ret != LocationError.None))
698             {
699                 Log.Error(Globals.LogTag, "Error in Setting Zone Changed Callback," + (LocationError)ret);
700                 throw LocationErrorFactory.ThrowLocationException(ret);
701             }
702         }
703
704         private void UnSetZoneChangedCallback()
705         {
706             Log.Info(Globals.LogTag, "Inside UnSetZoneChangedCallback");
707             int ret = Interop.LocatorEvent.UnSetZoneChangedCallback(_handle);
708             if (((LocationError)ret != LocationError.None))
709             {
710                 Log.Error(Globals.LogTag, "Error in UnSetting Zone Changed Callback," + (LocationError)ret);
711             }
712         }
713
714         /// <summary>
715         /// The SetttingChanged event is raised when the location setting is changed.
716         /// </summary>
717         /// <since_tizen> 3 </since_tizen>
718         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
719         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
720         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
721         public event EventHandler<SettingChangedEventArgs> SettingChanged
722         {
723             add
724             {
725                 Log.Info(Globals.LogTag, "Adding SettingChanged EventHandler");
726                 if (_settingChanged == null)
727                 {
728                     Log.Info(Globals.LogTag, "Calling function SetSettingChangedCallback");
729                     SetSettingChangedCallback();
730                 }
731                 _settingChanged += value;
732             }
733             remove
734             {
735                 Log.Info(Globals.LogTag, "Removing SettingChanged EventHandler");
736                 _settingChanged -= value;
737
738                 if (_settingChanged == null)
739                 {
740                     Log.Info(Globals.LogTag, "Calling function UnSetSettingChangedCallback");
741                     UnSetSettingChangedCallback();
742                 }
743             }
744         }
745
746         private void SetSettingChangedCallback()
747         {
748             Log.Info(Globals.LogTag, "Calling SetSettingChangedCallback");
749             if (_settingChangedCallback == null)
750             {
751                 _settingChangedCallback = (method, enable, userData) =>
752                 {
753                     Log.Info(Globals.LogTag, "Calling SettingChangedCallback");
754                     _settingChanged?.Invoke(this, new SettingChangedEventArgs(method, enable));
755                 };
756             }
757
758             int ret = Interop.LocatorEvent.SetSettingChangedCallback((int)_locationType, _settingChangedCallback, IntPtr.Zero);
759             if (((LocationError)ret != LocationError.None))
760             {
761                 Log.Error(Globals.LogTag, "Error in Setting Changed Callback," + (LocationError)ret);
762                 throw LocationErrorFactory.ThrowLocationException(ret);
763             }
764         }
765
766         private void UnSetSettingChangedCallback()
767         {
768             Log.Info(Globals.LogTag, "Calling UnSetSettingChangedCallback");
769             int ret = Interop.LocatorEvent.UnSetSettingChangedCallback((int)_locationType);
770             if (((LocationError)ret != LocationError.None))
771             {
772                 Log.Error(Globals.LogTag, "Error in Unsetting Setting's Callback," + (LocationError)ret);
773                 throw LocationErrorFactory.ThrowLocationException(ret);
774             }
775         }
776
777         /// <summary>
778         /// The DistanceBasedLocationChanged event is raised with the updated location information.
779         /// The callback will be invoked at a minimum interval or minimum distance with the updated position information.
780         /// </summary>
781         /// <since_tizen> 3 </since_tizen>
782         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
783         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
784         public event EventHandler<LocationChangedEventArgs> DistanceBasedLocationChanged
785         {
786             add
787             {
788                 Log.Info(Globals.LogTag, "Adding DistanceBasedLocationChanged EventHandler");
789                 //if (_distanceBasedLocationChanged == null)
790                 {
791                     Log.Info(Globals.LogTag, "Calling function SetDistanceBasedLocationChangedCallback");
792                     SetDistanceBasedLocationChangedCallback();
793                 }
794                 _distanceBasedLocationChanged += value;
795             }
796             remove
797             {
798                 Log.Info(Globals.LogTag, "Removing DistanceBasedLocationChanged EventHandler");
799                 _distanceBasedLocationChanged -= value;
800
801                 if (_distanceBasedLocationChanged == null)
802                 {
803                     Log.Info(Globals.LogTag, "Calling function UnSetDistanceBasedLocationChangedCallback");
804                     UnSetDistanceBasedLocationChangedCallback();
805                 }
806             }
807         }
808
809         private void SetDistanceBasedLocationChangedCallback()
810         {
811             Log.Info(Globals.LogTag, "SetDistanceBasedLocationChangedCallback");
812             if (_distanceBasedLocationChangedCallback == null) {
813                 _distanceBasedLocationChangedCallback = (latitude, longitude, altitude, speed, direction, accuracy, timestamp, userData) =>
814                 {
815                     Log.Info(Globals.LogTag, "DistanceBasedLocationChangedCallback #1");
816                     Location location = new Location(latitude, longitude, altitude, speed, direction, accuracy, timestamp);
817                     Log.Info(Globals.LogTag, "DistanceBasedLocationChangedCallback #2");
818                     _distanceBasedLocationChanged?.Invoke(this, new LocationChangedEventArgs(location));
819                     Log.Info(Globals.LogTag, "DistanceBasedLocationChangedCallback #3");
820                 };
821             }
822
823             int ret = Interop.LocatorEvent.SetDistanceBasedLocationChangedCallback(_handle, _distanceBasedLocationChangedCallback, _stayInterval, _distance, IntPtr.Zero);
824             if (((LocationError)ret != LocationError.None))
825             {
826                 Log.Error(Globals.LogTag, "Error in Setting Distance based location changed Callback," + (LocationError)ret);
827                 throw LocationErrorFactory.ThrowLocationException(ret);
828             }
829         }
830
831         private void UnSetDistanceBasedLocationChangedCallback()
832         {
833             Log.Info(Globals.LogTag, "UnSetDistanceBasedLocationChangedCallback");
834             int ret = Interop.LocatorEvent.UnSetDistanceBasedLocationChangedCallback(_handle);
835             if (((LocationError)ret != LocationError.None))
836             {
837                 Log.Error(Globals.LogTag, "Error in UnSetting Distance based location changed Callback," + (LocationError)ret);
838                 throw LocationErrorFactory.ThrowLocationException(ret);
839             }
840             _distanceBasedLocationChanged = null;
841         }
842
843         /// <summary>
844         /// The LocationUpdated event is raised at defined intervals of time with the updated location information.
845         /// The callback will be invoked periodically.
846         /// </summary>
847         /// <since_tizen> 3 </since_tizen>
848         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
849         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
850         public event EventHandler<LocationChangedEventArgs> LocationChanged
851         {
852             add
853             {
854                 Log.Info(Globals.LogTag, "Adding LocationChanged EventHandler");
855                 if (_batchPeriod > 0 && _batchPeriod > _batchInterval)
856                 {
857                     Log.Info(Globals.LogTag, "Calling function SetLocationBatchCallback");
858                     SetLocationBatchCallback();
859                 }
860                 else
861                 {
862                     Log.Info(Globals.LogTag, "Calling function SetLocationChangedCallback");
863                     SetLocationChangedCallback();
864                 }
865                 _locationChanged += value;
866             }
867             remove
868             {
869                 Log.Info(Globals.LogTag, "Removing LocationChanged EventHandler");
870                 _locationChanged -= value;
871
872                 if (_locationChanged == null)
873                 {
874                     if (_batchPeriod > 0 && _batchPeriod > _batchInterval)
875                     {
876                         Log.Info(Globals.LogTag, "Calling function UnSetLocationBatchCallback");
877                         UnSetLocationBatchCallback();
878                     }
879                     else
880                     {
881                         Log.Info(Globals.LogTag, "Calling function UnSetLocationChangedCallback");
882                         UnSetLocationChangedCallback();
883                     }
884                 }
885             }
886         }
887
888         private void SetLocationChangedCallback()
889         {
890             Log.Info(Globals.LogTag, "Calling SetLocationChangedCallback");
891
892             if (_locationChangedCallback == null) {
893                 _locationChangedCallback = (latitude, longitude, altitude, speed, direction, accuracy, timestamp, userData) =>
894                 {
895                     Log.Info(Globals.LogTag, "LocationChangedCallback has been called");
896                     Location location = new Location(latitude, longitude, altitude, speed, direction, accuracy, timestamp);
897                     _location = location;
898                     _locationChanged?.Invoke(this, new LocationChangedEventArgs(location));
899                 };
900             }
901
902             int ret = Interop.LocatorEvent.SetLocationChangedCallback(_handle, _locationChangedCallback, _interval, IntPtr.Zero);
903             if (((LocationError)ret != LocationError.None))
904             {
905                 Log.Error(Globals.LogTag, "Error in Setting location changed Callback," + (LocationError)ret);
906                 throw LocationErrorFactory.ThrowLocationException(ret);
907             }
908         }
909
910         private void UnSetLocationChangedCallback()
911         {
912             Log.Info(Globals.LogTag, "Calling UnSetLocationChangedCallback");
913             int ret = Interop.LocatorEvent.UnSetLocationChangedCallback(_handle);
914             if (((LocationError)ret != LocationError.None))
915             {
916                 Log.Error(Globals.LogTag, "Error in UnSetting location changed Callback," + (LocationError)ret);
917                 throw LocationErrorFactory.ThrowLocationException(ret);
918             }
919         }
920
921         private void SetLocationBatchCallback()
922         {
923             Log.Info(Globals.LogTag, "Calling SetLocationBatchCallback");
924             int ret;
925             if (_locationBatchCallback == null) {
926                 _locationBatchCallback = (batch_size, userData) =>
927                 {
928                     Log.Info(Globals.LogTag, "LocationBatchCallback has been called, size : " + batch_size);
929
930                     _locationBatchGetCallback = (latitude, longitude, altitude, speed, direction, horizontal, vertical, timestamp, batchUserData) =>
931                     {
932                         Log.Info(Globals.LogTag, "GetLocationBatch has been called");
933                         Location location = new Location(latitude, longitude, altitude, speed, direction, horizontal, timestamp);
934                         _location = location;
935                         _locationChanged?.Invoke(this, new LocationChangedEventArgs(location));
936                     };
937
938                     ret = Interop.LocatorEvent.GetLocationBatch(_handle, _locationBatchGetCallback, IntPtr.Zero);
939                     if (((LocationError)ret != LocationError.None))
940                     {
941                         Log.Error(Globals.LogTag, "Error in Setting location batch Callback," + (LocationError)ret);
942                         throw LocationErrorFactory.ThrowLocationException(ret);
943                     }
944                 };
945             }
946
947             ret = Interop.LocatorEvent.SetLocationBatchCallback(_handle, _locationBatchCallback, _batchInterval, _batchPeriod, IntPtr.Zero);
948             if (((LocationError)ret != LocationError.None))
949             {
950                 Log.Error(Globals.LogTag, "Error in Setting location batch Callback," + (LocationError)ret);
951                 throw LocationErrorFactory.ThrowLocationException(ret);
952             }
953         }
954
955         private void UnSetLocationBatchCallback()
956         {
957             Log.Info(Globals.LogTag, "Calling UnSetLocationBatchCallback");
958             int ret = Interop.LocatorEvent.UnSetLocationBatchCallback(_handle);
959             if (((LocationError)ret != LocationError.None))
960             {
961                 Log.Error(Globals.LogTag, "Error in UnSetting location batch Callback," + (LocationError)ret);
962                 throw LocationErrorFactory.ThrowLocationException(ret);
963             }
964         }
965     }
966 }