summaryrefslogtreecommitdiffstats
path: root/ios/chrome/browser/geolocation/location_manager.mm
blob: cab3ea7f1d571affde0f366e4605a8143cc64663 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#import "ios/chrome/browser/geolocation/location_manager.h"

#import "base/ios/weak_nsobject.h"
#include "base/mac/scoped_nsobject.h"
#import "ios/chrome/browser/geolocation/CLLocation+OmniboxGeolocation.h"
#import "ios/chrome/browser/geolocation/location_manager+Testing.h"
#import "ios/public/provider/chrome/browser/chrome_browser_provider.h"
#import "ios/public/provider/chrome/browser/geolocation_updater_provider.h"

namespace {

const CLLocationDistance kLocationDesiredAccuracy =
    kCLLocationAccuracyHundredMeters;
// Number of seconds to wait before automatically stopping location updates.
const NSTimeInterval kLocationStopUpdateDelay = 5.0;
// A large value to disable automatic location updates in GeolocationUpdater.
const NSTimeInterval kLocationUpdateInterval = 365.0 * 24.0 * 60.0 * 60.0;

}  // namespace

@interface LocationManager () {
  base::scoped_nsprotocol<id<GeolocationUpdater>> _locationUpdater;
  base::scoped_nsobject<CLLocation> _currentLocation;
  base::WeakNSProtocol<id<LocationManagerDelegate>> _delegate;
  base::scoped_nsobject<NSDate> _startTime;
}

// Handles GeolocationUpdater notification for an updated device location.
- (void)handleLocationUpdateNotification:(NSNotification*)notification;
// Handles GeolocationUpdater notification for ending device location updates.
- (void)handleLocationStopNotification:(NSNotification*)notification;
// Handles GeolocationUpdater notification for changing authorization.
- (void)handleAuthorizationChangeNotification:(NSNotification*)notification;

@end

@implementation LocationManager

- (id)init {
  self = [super init];
  if (self) {
    ios::GeolocationUpdaterProvider* provider =
        ios::GetChromeBrowserProvider()->GetGeolocationUpdaterProvider();

    // |provider| may be null in tests.
    if (provider) {
      _locationUpdater.reset(provider->CreateGeolocationUpdater(false));
      [_locationUpdater setDesiredAccuracy:kLocationDesiredAccuracy
                            distanceFilter:kLocationDesiredAccuracy / 2];
      [_locationUpdater setStopUpdateDelay:kLocationStopUpdateDelay];
      [_locationUpdater setUpdateInterval:kLocationUpdateInterval];

      NSNotificationCenter* defaultCenter =
          [NSNotificationCenter defaultCenter];
      [defaultCenter addObserver:self
                        selector:@selector(handleLocationUpdateNotification:)
                            name:provider->GetUpdateNotificationName()
                          object:_locationUpdater];
      [defaultCenter addObserver:self
                        selector:@selector(handleLocationStopNotification:)
                            name:provider->GetStopNotificationName()
                          object:_locationUpdater];
      [defaultCenter
          addObserver:self
             selector:@selector(handleAuthorizationChangeNotification:)
                 name:provider->GetAuthorizationChangeNotificationName()
               object:nil];
    }
  }
  return self;
}

- (void)dealloc {
  [[NSNotificationCenter defaultCenter] removeObserver:self];
  [super dealloc];
}

- (CLAuthorizationStatus)authorizationStatus {
  return [CLLocationManager authorizationStatus];
}

- (CLLocation*)currentLocation {
  if (!_currentLocation)
    _currentLocation.reset([[_locationUpdater currentLocation] retain]);
  return _currentLocation;
}

- (id<LocationManagerDelegate>)delegate {
  return _delegate;
}

- (void)setDelegate:(id<LocationManagerDelegate>)delegate {
  _delegate.reset(delegate);
}

- (BOOL)locationServicesEnabled {
  return [CLLocationManager locationServicesEnabled];
}

- (void)startUpdatingLocation {
  CLLocation* currentLocation = self.currentLocation;
  if (!currentLocation || [currentLocation cr_shouldRefresh]) {
    if (![_locationUpdater isEnabled])
      _startTime.reset([[NSDate alloc] init]);

    [_locationUpdater requestWhenInUseAuthorization];
    [_locationUpdater setEnabled:YES];
  }
}

- (void)stopUpdatingLocation {
  [_locationUpdater setEnabled:NO];
}

#pragma mark - Private

- (void)handleLocationUpdateNotification:(NSNotification*)notification {
  NSString* newLocationKey = ios::GetChromeBrowserProvider()
                                 ->GetGeolocationUpdaterProvider()
                                 ->GetUpdateNewLocationKey();
  CLLocation* location = [[notification userInfo] objectForKey:newLocationKey];
  if (location) {
    _currentLocation.reset([location retain]);

    if (_startTime) {
      NSTimeInterval interval = -[_startTime timeIntervalSinceNow];
      [_currentLocation cr_setAcquisitionInterval:interval];
    }
  }
}

- (void)handleLocationStopNotification:(NSNotification*)notification {
  [_locationUpdater setEnabled:NO];
}

- (void)handleAuthorizationChangeNotification:(NSNotification*)notification {
  [_delegate locationManagerDidChangeAuthorizationStatus:self];
}

#pragma mark - LocationManager+Testing

- (void)setGeolocationUpdater:(id<GeolocationUpdater>)geolocationUpdater {
  _locationUpdater.reset([geolocationUpdater retain]);
}

@end