|  | // 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" | 
|  |  | 
|  | #include "ios/chrome/app/tests_hook.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" | 
|  |  | 
|  | #if !defined(__has_feature) || !__has_feature(objc_arc) | 
|  | #error "This file requires ARC support." | 
|  | #endif | 
|  |  | 
|  | 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 () { | 
|  | id<GeolocationUpdater> _locationUpdater; | 
|  | 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 | 
|  | @synthesize delegate = _delegate; | 
|  | @synthesize currentLocation = _currentLocation; | 
|  |  | 
|  | - (id)init { | 
|  | self = [super init]; | 
|  | if (self) { | 
|  | ios::GeolocationUpdaterProvider* provider = | 
|  | ios::GetChromeBrowserProvider()->GetGeolocationUpdaterProvider(); | 
|  |  | 
|  | // |provider| may be null in tests. | 
|  | if (provider) { | 
|  | _locationUpdater = 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; | 
|  | } | 
|  |  | 
|  | - (CLAuthorizationStatus)authorizationStatus { | 
|  | return [CLLocationManager authorizationStatus]; | 
|  | } | 
|  |  | 
|  | - (CLLocation*)currentLocation { | 
|  | if (!_currentLocation) | 
|  | _currentLocation = [_locationUpdater currentLocation]; | 
|  | return _currentLocation; | 
|  | } | 
|  |  | 
|  | - (BOOL)locationServicesEnabled { | 
|  | return !tests_hook::DisableGeolocation() && | 
|  | [CLLocationManager locationServicesEnabled]; | 
|  | } | 
|  |  | 
|  | - (void)startUpdatingLocation { | 
|  | CLLocation* currentLocation = self.currentLocation; | 
|  | if (!currentLocation || [currentLocation cr_shouldRefresh]) { | 
|  | if (![_locationUpdater isEnabled]) | 
|  | _startTime = [[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 = location; | 
|  |  | 
|  | 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 = geolocationUpdater; | 
|  | } | 
|  |  | 
|  | @end |