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