[NUI] TCSACR-226 code change (#1032)
[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 _isBatchStarted;
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> 4 </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> 4 </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="UnauthorizedAccessException">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="UnauthorizedAccessException">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             _isBatchStarted = (_batchPeriod > 0 ) && (_batchPeriod > _batchInterval);
326             if (_isBatchStarted)
327             {
328                 int ret = Interop.Locator.StartBatch(_handle);
329                 if (((LocationError)ret != LocationError.None))
330                 {
331                     Log.Error(Globals.LogTag, "Error Starting Location Batch mode," + (LocationError)ret);
332                     throw LocationErrorFactory.ThrowLocationException(ret);
333                 }
334             }
335             else
336             {
337                 int ret = Interop.Locator.Start(_handle);
338                    if (((LocationError)ret != LocationError.None))
339                 {
340                     Log.Error(Globals.LogTag, "Error Starting Location Manager," + (LocationError)ret);
341                     throw LocationErrorFactory.ThrowLocationException(ret);
342                 }
343             }
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             if (_isBatchStarted)
358             {
359                 int ret = Interop.Locator.StopBatch(_handle);
360                 if (((LocationError)ret != LocationError.None))
361                 {
362                     Log.Error(Globals.LogTag, "Error Stopping Location Batch mode," + (LocationError)ret);
363                     throw LocationErrorFactory.ThrowLocationException(ret);
364                 }
365             }
366             else
367             {
368                 int ret = Interop.Locator.Stop(_handle);
369                 if (((LocationError)ret != LocationError.None))
370                 {
371                     Log.Error(Globals.LogTag, "Error stopping Location Manager," + (LocationError)ret);
372                     throw LocationErrorFactory.ThrowLocationException(ret);
373                 }
374             } 
375         }
376
377         /// <summary>
378         /// Sets a mock location for the given location method.
379         /// </summary>
380         /// <since_tizen> 3 </since_tizen>
381         /// <param name="location"> The location object containing the mock location details.</param>
382         /// <privilege>http://tizen.org/privilege/location</privilege>
383         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
384         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
385         /// <exception cref="UnauthorizedAccessException">Thrown when the application has no privilege to use the location.</exception>
386         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
387         public void SetMockLocation(Location location)
388         {
389             Log.Info(Globals.LogTag, "Setting mock location");
390             int ret = Interop.Locator.SetMockLocation(_handle, location.Latitude, location.Longitude, location.Altitude, location.Speed, location.Direction, location.Accuracy);
391             if (((LocationError)ret == LocationError.None))
392             {
393                 _location.Latitude = location.Latitude;
394                 _location.Longitude = location.Longitude;
395                 _location.Altitude = location.Altitude;
396                 _location.Speed = location.Speed;
397                 _location.Direction = location.Direction;
398                 _location.Accuracy = location.Accuracy;
399             }
400             else
401             {
402                 Log.Error(Globals.LogTag, "Error in setting up location mocking," + (LocationError)ret);
403                 throw LocationErrorFactory.ThrowLocationException(ret);
404             }
405         }
406
407         /// <summary>
408         /// Clears a mock location for the given location method.
409         /// </summary>
410         /// <since_tizen> 3 </since_tizen>
411         /// <privilege>http://tizen.org/privilege/location</privilege>
412         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
413         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
414         /// <exception cref="UnauthorizedAccessException">Thrown when the application has no privilege to use the location.</exception>
415         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
416         public void ClearMock()
417         {
418             Log.Info(Globals.LogTag, "Clear mock location");
419             int ret = Interop.Locator.ClearMock(_handle);
420             if (((LocationError)ret != LocationError.None))
421             {
422                 Log.Error(Globals.LogTag, "Error in clear up location mocking," + (LocationError)ret);
423                 throw LocationErrorFactory.ThrowLocationException(ret);
424             }
425         }
426
427         /// <summary>
428         /// Gets the details of the location asynchronously.
429         /// </summary>
430         /// <since_tizen> 3 </since_tizen>
431         /// <param name="timeout"> Timeout to stop requesting a single location after (seconds).</param>
432         /// <returns> A task which contains the current location details.</returns>
433         /// <privilege>http://tizen.org/privilege/location</privilege>
434         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
435         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
436         /// <exception cref="UnauthorizedAccessException">Thrown when the application has no privilege to use the location.</exception>
437         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
438         public Task<Location> GetLocationAsync(int timeout)
439         {
440             var task = new TaskCompletionSource<Location>();
441             IntPtr id = IntPtr.Zero;
442             lock (_callback_map)
443             {
444                 id = (IntPtr)_requestId++;
445                 _callback_map[id] = (LocationError error, double latitude, double longitude, double altitude, int timestamp, double speed, double direction, double climb, IntPtr userData) =>
446                 {
447                     if (error != LocationError.None)
448                     {
449                         Log.Error(Globals.LogTag, "Error in getting up location information," + (LocationError)error);
450                     }
451                     else
452                     {
453                         Log.Info(Globals.LogTag, "Creating a current location object");
454                         _location = new Location(latitude, longitude, altitude, speed, direction, 0.0, timestamp);
455                         task.SetResult(_location);
456                     }
457                     lock (_callback_map)
458                     {
459                         _callback_map.Remove(userData);
460                     }
461                 };
462             }
463
464             int ret = Interop.LocatorEvent.GetSingleLocation(_handle, timeout, _callback_map[id], id);
465             if (((LocationError)ret != LocationError.None))
466             {
467                 Log.Error(Globals.LogTag, "Error in setting up location mocking," + (LocationError)ret);
468                 throw LocationErrorFactory.ThrowLocationException(ret);
469             }
470             return task.Task;
471         }
472
473
474         /// <summary>
475         /// Gets the details of the location.
476         /// </summary>
477         /// <since_tizen> 3 </since_tizen>
478         /// <returns> Which contains the current location details.</returns>
479         /// <privilege>http://tizen.org/privilege/location</privilege>
480         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
481         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
482         /// <exception cref="UnauthorizedAccessException">Thrown when the application has no privilege to use the location.</exception>
483         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
484         public Location GetLocation()
485         {
486             double latitude = 0;
487             double longitude = 0;
488             double altitude = 0;
489             double climb = 0;
490             double speed = 0;
491             double direction = 0;
492             int level = 0;
493             double accuracy = 0;
494             double vertical = 0;
495             int timestamp = 0;
496
497             Log.Info(Globals.LogTag, "Get current location information");
498             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);
499             if (((LocationError)ret != LocationError.None))
500             {
501                 if ((LocationError)ret == LocationError.ServiceNotAvailable)
502                 {
503                     Log.Info(Globals.LogTag, "Get last location information");
504                     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);
505                     if (((LocationError)ret != LocationError.None))
506                     {
507                         Log.Error(Globals.LogTag, "Error in get last location information," + (LocationError)ret);
508                         throw LocationErrorFactory.ThrowLocationException(ret);
509                     }
510                     else
511                     {
512                         if (latitude == 0.0 && longitude == 0.0)
513                         {
514                             Log.Error(Globals.LogTag, "Error fail to get last location information");
515                             throw LocationErrorFactory.ThrowLocationException((int)LocationError.ServiceNotAvailable);
516                         }
517                     }
518                 }
519                 else
520                 {
521                     Log.Error(Globals.LogTag, "Error in get current location information," + (LocationError)ret);
522                     throw LocationErrorFactory.ThrowLocationException(ret);
523                 }
524             }
525
526             Location location = new Location(latitude, longitude, altitude, speed, direction, accuracy, timestamp);
527             _location = location;
528
529             return location;
530         }
531
532
533         /// <summary>
534         /// Adds a bound for a given locator.
535         /// </summary>
536         /// <since_tizen> 3 </since_tizen>
537         /// <param name="locationBoundary">The boundary object to be added to the locator.</param>
538         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
539         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
540         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
541         public void AddBoundary(LocationBoundary locationBoundary)
542         {
543             Log.Info(Globals.LogTag, "AddBoundary called");
544
545             int ret = Interop.Locator.AddBoundary(_handle, locationBoundary.GetHandle());
546             if ((LocationBoundError)ret != LocationBoundError.None)
547             {
548                 Log.Error(Globals.LogTag, "Error Adding Boundary," + (LocationBoundError)ret);
549                 throw LocationErrorFactory.ThrowLocationBoundaryException(ret);
550             }
551         }
552
553         /// <summary>
554         /// Deletes a bound for a given locator.
555         /// </summary>
556         /// <since_tizen> 3 </since_tizen>
557         /// <param name="locationBoundary"> The boundary object to be removed from the locator.</param>
558         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
559         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
560         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
561         public void RemoveBoundary(LocationBoundary locationBoundary)
562         {
563             Log.Info(Globals.LogTag, "RemoveBoundary called");
564             int ret = Interop.Locator.RemoveBoundary(_handle, locationBoundary.GetHandle());
565             if ((LocationBoundError)ret != LocationBoundError.None)
566             {
567                 Log.Error(Globals.LogTag, "Error Removing Boundary," + (LocationBoundError)ret);
568                 throw LocationErrorFactory.ThrowLocationBoundaryException(ret);
569             }
570         }
571
572         /// <summary>
573         /// The overidden Dispose method of the IDisposable class.
574         /// </summary>
575         /// <since_tizen> 3 </since_tizen>
576         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
577         public void Dispose()
578         {
579             Dispose(true);
580             GC.SuppressFinalize(this);
581         }
582
583         /// <summary>
584         /// Dispose.
585         /// </summary>
586         /// <since_tizen> 3 </since_tizen>
587         protected virtual void Dispose(bool disposing)
588         {
589             if (_disposed)
590                 return;
591
592             if (disposing)
593                 DestroyHandle();
594
595             _disposed = true;
596         }
597
598         private void DestroyHandle()
599         {
600             int ret = Interop.Locator.Destroy(_handle);
601             if (((LocationError)ret != LocationError.None))
602             {
603                 Log.Error(Globals.LogTag, "Error in Destroy handle, " + (LocationError)ret);
604                 throw LocationErrorFactory.ThrowLocationException(ret);
605             }
606         }
607
608         /// <summary>
609         /// The ServiceStateChanged event is invoked when the location service state is changed.
610         /// </summary>
611         /// <since_tizen> 3 </since_tizen>
612         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
613         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
614         public event EventHandler<ServiceStateChangedEventArgs> ServiceStateChanged
615         {
616             add
617             {
618                 Log.Info(Globals.LogTag, "ServiceStateChanged called");
619                 if (_serviceStateChanged == null)
620                 {
621                     Log.Info(Globals.LogTag, "Calling function SetServiceStateChangedCallback");
622                     SetServiceStateChangedCallback();
623                 }
624                 _serviceStateChanged += value;
625             }
626             remove
627             {
628                 Log.Info(Globals.LogTag, "Callback removed");
629                 _serviceStateChanged -= value;
630
631                 if (_serviceStateChanged == null)
632                 {
633                     Log.Info(Globals.LogTag, "Calling function UnSetServiceStateChangedCallback");
634                     UnSetServiceStateChangedCallback();
635                 }
636             }
637         }
638
639         private void SetServiceStateChangedCallback()
640         {
641             Log.Info(Globals.LogTag, "Calling Interop.LocatorEvent.SetServiceStateChangedCallback");
642             if (_serviceStateChangedCallback == null)
643             {
644                 _serviceStateChangedCallback = (state, userData) =>
645                 {
646                     Log.Info(Globals.LogTag, "Inside ServiceStateChangedCallback");
647                     _serviceStateChanged?.Invoke(this, new ServiceStateChangedEventArgs(state));
648                 };
649             }
650
651             int ret = Interop.LocatorEvent.SetServiceStateChangedCallback(_handle, _serviceStateChangedCallback, IntPtr.Zero);
652             if (((LocationError)ret != LocationError.None))
653             {
654                 Log.Error(Globals.LogTag, "Error in Setting Service State Changed Callback," + (LocationError)ret);
655                 throw LocationErrorFactory.ThrowLocationException(ret);
656             }
657         }
658
659         private void UnSetServiceStateChangedCallback()
660         {
661             Log.Info(Globals.LogTag, "Calling Interop.LocatorEvent.UnSetServiceStateChangedCallback");
662             int ret = Interop.LocatorEvent.UnSetServiceStateChangedCallback(_handle);
663             if (((LocationError)ret != LocationError.None))
664             {
665                 Log.Error(Globals.LogTag, "Error in UnSetting Service State Changed Callback," + (LocationError)ret);
666                 throw LocationErrorFactory.ThrowLocationException(ret);
667             }
668         }
669
670         /// <summary>
671         /// The ZoneChanged event is invoked when the previously set boundary area is entered or left.
672         /// </summary>
673         /// <since_tizen> 3 </since_tizen>
674         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
675         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
676         public event EventHandler<ZoneChangedEventArgs> ZoneChanged
677         {
678             add
679             {
680                 Log.Info(Globals.LogTag, "ZoneChanged called");
681                 if (_zoneChanged == null)
682                 {
683                     Log.Info(Globals.LogTag, "Calling function SetZoneChangedCallback");
684                     SetZoneChangedCallback();
685                 }
686                 _zoneChanged += value;
687             }
688             remove
689             {
690                 Log.Info(Globals.LogTag, "Callback removed");
691                 _zoneChanged -= value;
692
693                 if (_zoneChanged == null)
694                 {
695                     Log.Info(Globals.LogTag, "Calling function UnSetZoneChangedCallback");
696                     UnSetZoneChangedCallback();
697                 }
698             }
699         }
700
701         private void SetZoneChangedCallback()
702         {
703             Log.Info(Globals.LogTag, "Inside SetZoneChangedCallback");
704             if (_zoneChangedCallback == null)
705             {
706                 _zoneChangedCallback = (state, latitude, longitude, altitude, timestamp, userData) =>
707                 {
708                     Log.Info(Globals.LogTag, "Inside ZoneChangedCallback");
709                     DateTime timeStamp = DateTime.Now;
710                     if (timestamp != 0)
711                     {
712                         DateTime start = DateTime.SpecifyKind(new DateTime(1970, 1, 1).AddSeconds(timestamp), DateTimeKind.Utc);
713                         timeStamp = start.ToLocalTime();
714                     }
715                     _zoneChanged?.Invoke(this, new ZoneChangedEventArgs(state, latitude, longitude, altitude, timeStamp));
716                 };
717             }
718
719             int ret = Interop.LocatorEvent.SetZoneChangedCallback(_handle, _zoneChangedCallback, IntPtr.Zero);
720             if (((LocationError)ret != LocationError.None))
721             {
722                 Log.Error(Globals.LogTag, "Error in Setting Zone Changed Callback," + (LocationError)ret);
723                 throw LocationErrorFactory.ThrowLocationException(ret);
724             }
725         }
726
727         private void UnSetZoneChangedCallback()
728         {
729             Log.Info(Globals.LogTag, "Inside UnSetZoneChangedCallback");
730             int ret = Interop.LocatorEvent.UnSetZoneChangedCallback(_handle);
731             if (((LocationError)ret != LocationError.None))
732             {
733                 Log.Error(Globals.LogTag, "Error in UnSetting Zone Changed Callback," + (LocationError)ret);
734             }
735         }
736
737         /// <summary>
738         /// The SetttingChanged event is raised when the location setting is changed.
739         /// </summary>
740         /// <since_tizen> 3 </since_tizen>
741         /// <exception cref="InvalidOperationException">Thrown when the operation is invalid for the current state.</exception>
742         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
743         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
744         public event EventHandler<SettingChangedEventArgs> SettingChanged
745         {
746             add
747             {
748                 Log.Info(Globals.LogTag, "Adding SettingChanged EventHandler");
749                 if (_settingChanged == null)
750                 {
751                     Log.Info(Globals.LogTag, "Calling function SetSettingChangedCallback");
752                     SetSettingChangedCallback();
753                 }
754                 _settingChanged += value;
755             }
756             remove
757             {
758                 Log.Info(Globals.LogTag, "Removing SettingChanged EventHandler");
759                 _settingChanged -= value;
760
761                 if (_settingChanged == null)
762                 {
763                     Log.Info(Globals.LogTag, "Calling function UnSetSettingChangedCallback");
764                     UnSetSettingChangedCallback();
765                 }
766             }
767         }
768
769         private void SetSettingChangedCallback()
770         {
771             Log.Info(Globals.LogTag, "Calling SetSettingChangedCallback");
772             if (_settingChangedCallback == null)
773             {
774                 _settingChangedCallback = (method, enable, userData) =>
775                 {
776                     Log.Info(Globals.LogTag, "Calling SettingChangedCallback");
777                     _settingChanged?.Invoke(this, new SettingChangedEventArgs(method, enable));
778                 };
779             }
780
781             int ret = Interop.LocatorEvent.SetSettingChangedCallback((int)_locationType, _settingChangedCallback, IntPtr.Zero);
782             if (((LocationError)ret != LocationError.None))
783             {
784                 Log.Error(Globals.LogTag, "Error in Setting Changed Callback," + (LocationError)ret);
785                 throw LocationErrorFactory.ThrowLocationException(ret);
786             }
787         }
788
789         private void UnSetSettingChangedCallback()
790         {
791             Log.Info(Globals.LogTag, "Calling UnSetSettingChangedCallback");
792             int ret = Interop.LocatorEvent.UnSetSettingChangedCallback((int)_locationType);
793             if (((LocationError)ret != LocationError.None))
794             {
795                 Log.Error(Globals.LogTag, "Error in Unsetting Setting's Callback," + (LocationError)ret);
796                 throw LocationErrorFactory.ThrowLocationException(ret);
797             }
798         }
799
800         /// <summary>
801         /// The DistanceBasedLocationChanged event is raised with the updated location information.
802         /// The callback will be invoked at a minimum interval or minimum distance with the updated position information.
803         /// </summary>
804         /// <since_tizen> 3 </since_tizen>
805         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
806         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
807         public event EventHandler<LocationChangedEventArgs> DistanceBasedLocationChanged
808         {
809             add
810             {
811                 Log.Info(Globals.LogTag, "Adding DistanceBasedLocationChanged EventHandler");
812                 //if (_distanceBasedLocationChanged == null)
813                 {
814                     Log.Info(Globals.LogTag, "Calling function SetDistanceBasedLocationChangedCallback");
815                     SetDistanceBasedLocationChangedCallback();
816                 }
817                 _distanceBasedLocationChanged += value;
818             }
819             remove
820             {
821                 Log.Info(Globals.LogTag, "Removing DistanceBasedLocationChanged EventHandler");
822                 _distanceBasedLocationChanged -= value;
823
824                 if (_distanceBasedLocationChanged == null)
825                 {
826                     Log.Info(Globals.LogTag, "Calling function UnSetDistanceBasedLocationChangedCallback");
827                     UnSetDistanceBasedLocationChangedCallback();
828                 }
829             }
830         }
831
832         private void SetDistanceBasedLocationChangedCallback()
833         {
834             Log.Info(Globals.LogTag, "SetDistanceBasedLocationChangedCallback");
835             if (_distanceBasedLocationChangedCallback == null) {
836                 _distanceBasedLocationChangedCallback = (latitude, longitude, altitude, speed, direction, accuracy, timestamp, userData) =>
837                 {
838                     Log.Info(Globals.LogTag, "DistanceBasedLocationChangedCallback #1");
839                     Location location = new Location(latitude, longitude, altitude, speed, direction, accuracy, timestamp);
840                     Log.Info(Globals.LogTag, "DistanceBasedLocationChangedCallback #2");
841                     _distanceBasedLocationChanged?.Invoke(this, new LocationChangedEventArgs(location));
842                     Log.Info(Globals.LogTag, "DistanceBasedLocationChangedCallback #3");
843                 };
844             }
845
846             int ret = Interop.LocatorEvent.SetDistanceBasedLocationChangedCallback(_handle, _distanceBasedLocationChangedCallback, _stayInterval, _distance, IntPtr.Zero);
847             if (((LocationError)ret != LocationError.None))
848             {
849                 Log.Error(Globals.LogTag, "Error in Setting Distance based location changed Callback," + (LocationError)ret);
850                 throw LocationErrorFactory.ThrowLocationException(ret);
851             }
852         }
853
854         private void UnSetDistanceBasedLocationChangedCallback()
855         {
856             Log.Info(Globals.LogTag, "UnSetDistanceBasedLocationChangedCallback");
857             int ret = Interop.LocatorEvent.UnSetDistanceBasedLocationChangedCallback(_handle);
858             if (((LocationError)ret != LocationError.None))
859             {
860                 Log.Error(Globals.LogTag, "Error in UnSetting Distance based location changed Callback," + (LocationError)ret);
861                 throw LocationErrorFactory.ThrowLocationException(ret);
862             }
863             _distanceBasedLocationChanged = null;
864         }
865
866         /// <summary>
867         /// The LocationUpdated event is raised at defined intervals of time with the updated location information.
868         /// The callback will be invoked periodically.
869         /// </summary>
870         /// <since_tizen> 3 </since_tizen>
871         /// <exception cref="ArgumentException">Thrown when an invalid argument is used.</exception>
872         /// <exception cref="NotSupportedException">Thrown when the location is not supported.</exception>
873         public event EventHandler<LocationChangedEventArgs> LocationChanged
874         {
875             add
876             {
877                 Log.Info(Globals.LogTag, "Adding LocationChanged EventHandler");
878                 if (_batchPeriod > 0 && _batchPeriod > _batchInterval)
879                 {
880                     Log.Info(Globals.LogTag, "Calling function SetLocationBatchCallback");
881                     SetLocationBatchCallback();
882                 }
883                 else
884                 {
885                     Log.Info(Globals.LogTag, "Calling function SetLocationChangedCallback");
886                     SetLocationChangedCallback();
887                 }
888                 _locationChanged += value;
889             }
890             remove
891             {
892                 Log.Info(Globals.LogTag, "Removing LocationChanged EventHandler");
893                 _locationChanged -= value;
894
895                 if (_locationChanged == null)
896                 {
897                     if (_batchPeriod > 0 && _batchPeriod > _batchInterval)
898                     {
899                         Log.Info(Globals.LogTag, "Calling function UnSetLocationBatchCallback");
900                         UnSetLocationBatchCallback();
901                     }
902                     else
903                     {
904                         Log.Info(Globals.LogTag, "Calling function UnSetLocationChangedCallback");
905                         UnSetLocationChangedCallback();
906                     }
907                 }
908             }
909         }
910
911         private void SetLocationChangedCallback()
912         {
913             Log.Info(Globals.LogTag, "Calling SetLocationChangedCallback");
914
915             if (_locationChangedCallback == null) {
916                 _locationChangedCallback = (latitude, longitude, altitude, speed, direction, accuracy, timestamp, userData) =>
917                 {
918                     Log.Info(Globals.LogTag, "LocationChangedCallback has been called");
919                     Location location = new Location(latitude, longitude, altitude, speed, direction, accuracy, timestamp);
920                     _location = location;
921                     _locationChanged?.Invoke(this, new LocationChangedEventArgs(location));
922                 };
923             }
924
925             int ret = Interop.LocatorEvent.SetLocationChangedCallback(_handle, _locationChangedCallback, _interval, IntPtr.Zero);
926             if (((LocationError)ret != LocationError.None))
927             {
928                 Log.Error(Globals.LogTag, "Error in Setting location changed Callback," + (LocationError)ret);
929                 throw LocationErrorFactory.ThrowLocationException(ret);
930             }
931         }
932
933         private void UnSetLocationChangedCallback()
934         {
935             Log.Info(Globals.LogTag, "Calling UnSetLocationChangedCallback");
936             int ret = Interop.LocatorEvent.UnSetLocationChangedCallback(_handle);
937             if (((LocationError)ret != LocationError.None))
938             {
939                 Log.Error(Globals.LogTag, "Error in UnSetting location changed Callback," + (LocationError)ret);
940                 throw LocationErrorFactory.ThrowLocationException(ret);
941             }
942         }
943
944         private void SetLocationBatchCallback()
945         {
946             Log.Info(Globals.LogTag, "Calling SetLocationBatchCallback");
947             int ret;
948             if (_locationBatchCallback == null) {
949                 _locationBatchCallback = (batch_size, userData) =>
950                 {
951                     Log.Info(Globals.LogTag, "LocationBatchCallback has been called, size : " + batch_size);
952
953                     _locationBatchGetCallback = (latitude, longitude, altitude, speed, direction, horizontal, vertical, timestamp, batchUserData) =>
954                     {
955                         Log.Info(Globals.LogTag, "GetLocationBatch has been called");
956                         Location location = new Location(latitude, longitude, altitude, speed, direction, horizontal, timestamp);
957                         _location = location;
958                         _locationChanged?.Invoke(this, new LocationChangedEventArgs(location));
959                     };
960
961                     ret = Interop.LocatorEvent.GetLocationBatch(_handle, _locationBatchGetCallback, IntPtr.Zero);
962                     if (((LocationError)ret != LocationError.None))
963                     {
964                         Log.Error(Globals.LogTag, "Error in Setting location batch Callback," + (LocationError)ret);
965                         throw LocationErrorFactory.ThrowLocationException(ret);
966                     }
967                 };
968             }
969
970             ret = Interop.LocatorEvent.SetLocationBatchCallback(_handle, _locationBatchCallback, _batchInterval, _batchPeriod, IntPtr.Zero);
971             if (((LocationError)ret != LocationError.None))
972             {
973                 Log.Error(Globals.LogTag, "Error in Setting location batch Callback," + (LocationError)ret);
974                 throw LocationErrorFactory.ThrowLocationException(ret);
975             }
976         }
977
978         private void UnSetLocationBatchCallback()
979         {
980             Log.Info(Globals.LogTag, "Calling UnSetLocationBatchCallback");
981             int ret = Interop.LocatorEvent.UnSetLocationBatchCallback(_handle);
982             if (((LocationError)ret != LocationError.None))
983             {
984                 Log.Error(Globals.LogTag, "Error in UnSetting location batch Callback," + (LocationError)ret);
985                 throw LocationErrorFactory.ThrowLocationException(ret);
986             }
987         }
988     }
989 }