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