1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import "ios/chrome/browser/geolocation/location_manager.h"
6
7#include "ios/chrome/app/tests_hook.h"
8#import "ios/chrome/browser/geolocation/CLLocation+OmniboxGeolocation.h"
9#import "ios/chrome/browser/geolocation/location_manager+Testing.h"
10#import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
11#import "ios/public/provider/chrome/browser/geolocation_updater_provider.h"
12
13#if !defined(__has_feature) || !__has_feature(objc_arc)
14#error "This file requires ARC support."
15#endif
16
17namespace {
18
19const CLLocationDistance kLocationDesiredAccuracy =
20    kCLLocationAccuracyHundredMeters;
21// Number of seconds to wait before automatically stopping location updates.
22const NSTimeInterval kLocationStopUpdateDelay = 5.0;
23// A large value to disable automatic location updates in GeolocationUpdater.
24const NSTimeInterval kLocationUpdateInterval = 365.0 * 24.0 * 60.0 * 60.0;
25
26}  // namespace
27
28@interface LocationManager () {
29  id<GeolocationUpdater> _locationUpdater;
30  NSDate* _startTime;
31}
32
33// Handles GeolocationUpdater notification for an updated device location.
34- (void)handleLocationUpdateNotification:(NSNotification*)notification;
35// Handles GeolocationUpdater notification for ending device location updates.
36- (void)handleLocationStopNotification:(NSNotification*)notification;
37// Handles GeolocationUpdater notification for changing authorization.
38- (void)handleAuthorizationChangeNotification:(NSNotification*)notification;
39
40@end
41
42@implementation LocationManager
43@synthesize delegate = _delegate;
44@synthesize currentLocation = _currentLocation;
45
46- (id)init {
47  self = [super init];
48  if (self) {
49    ios::GeolocationUpdaterProvider* provider =
50        ios::GetChromeBrowserProvider()->GetGeolocationUpdaterProvider();
51
52    // |provider| may be null in tests.
53    if (provider) {
54      _locationUpdater = provider->CreateGeolocationUpdater(false);
55      [_locationUpdater setDesiredAccuracy:kLocationDesiredAccuracy
56                            distanceFilter:kLocationDesiredAccuracy / 2];
57      [_locationUpdater setStopUpdateDelay:kLocationStopUpdateDelay];
58      [_locationUpdater setUpdateInterval:kLocationUpdateInterval];
59
60      NSNotificationCenter* defaultCenter =
61          [NSNotificationCenter defaultCenter];
62      [defaultCenter addObserver:self
63                        selector:@selector(handleLocationUpdateNotification:)
64                            name:provider->GetUpdateNotificationName()
65                          object:_locationUpdater];
66      [defaultCenter addObserver:self
67                        selector:@selector(handleLocationStopNotification:)
68                            name:provider->GetStopNotificationName()
69                          object:_locationUpdater];
70      [defaultCenter
71          addObserver:self
72             selector:@selector(handleAuthorizationChangeNotification:)
73                 name:provider->GetAuthorizationChangeNotificationName()
74               object:nil];
75    }
76  }
77  return self;
78}
79
80- (CLAuthorizationStatus)authorizationStatus {
81  return [CLLocationManager authorizationStatus];
82}
83
84- (CLLocation*)currentLocation {
85  if (!_currentLocation)
86    _currentLocation = [_locationUpdater currentLocation];
87  return _currentLocation;
88}
89
90- (BOOL)locationServicesEnabled {
91  return !tests_hook::DisableGeolocation() &&
92         [CLLocationManager locationServicesEnabled];
93}
94
95- (void)startUpdatingLocation {
96  CLLocation* currentLocation = self.currentLocation;
97  if (!currentLocation || [currentLocation cr_shouldRefresh]) {
98    if (![_locationUpdater isEnabled])
99      _startTime = [[NSDate alloc] init];
100
101    [_locationUpdater requestWhenInUseAuthorization];
102    [_locationUpdater setEnabled:YES];
103  }
104}
105
106- (void)stopUpdatingLocation {
107  [_locationUpdater setEnabled:NO];
108}
109
110#pragma mark - Private
111
112- (void)handleLocationUpdateNotification:(NSNotification*)notification {
113  NSString* newLocationKey = ios::GetChromeBrowserProvider()
114                                 ->GetGeolocationUpdaterProvider()
115                                 ->GetUpdateNewLocationKey();
116  CLLocation* location = [[notification userInfo] objectForKey:newLocationKey];
117  if (location) {
118    _currentLocation = location;
119
120    if (_startTime) {
121      NSTimeInterval interval = -[_startTime timeIntervalSinceNow];
122      [_currentLocation cr_setAcquisitionInterval:interval];
123    }
124  }
125}
126
127- (void)handleLocationStopNotification:(NSNotification*)notification {
128  [_locationUpdater setEnabled:NO];
129}
130
131- (void)handleAuthorizationChangeNotification:(NSNotification*)notification {
132  [_delegate locationManagerDidChangeAuthorizationStatus:self];
133}
134
135#pragma mark - LocationManager+Testing
136
137- (void)setGeolocationUpdater:(id<GeolocationUpdater>)geolocationUpdater {
138  _locationUpdater = geolocationUpdater;
139}
140
141@end
142