[ios] Refactored location manager.

This commit is contained in:
Ilya Grechuhin 2016-06-29 10:36:48 +03:00
parent bdf88faf8b
commit 720165a8c3
37 changed files with 1076 additions and 1159 deletions

View file

@ -1,7 +1,6 @@
#import "LocationManager.h"
#import "MWMTableViewController.h"
@interface BookmarksVC : MWMTableViewController <LocationObserver, UITextFieldDelegate>
@interface BookmarksVC : MWMTableViewController <UITextFieldDelegate>
{
size_t m_categoryIndex;
}

View file

@ -5,6 +5,8 @@
#import "MapsAppDelegate.h"
#import "MapViewController.h"
#import "MWMBookmarkNameCell.h"
#import "MWMLocationHelpers.h"
#import "MWMLocationManager.h"
#import "MWMMapViewControlsManager.h"
#import "Statistics.h"
#import "UIColor+MapsMeColor.h"
@ -25,7 +27,7 @@
extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotification";
@interface BookmarksVC() <MFMailComposeViewControllerDelegate>
@interface BookmarksVC() <MFMailComposeViewControllerDelegate, MWMLocationObserver>
{
int m_trackSection;
int m_bookmarkSection;
@ -48,11 +50,6 @@ extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotifi
return self;
}
- (LocationManager *)locationManager
{
return [MapsAppDelegate theApp].locationManager;
}
- (void)viewDidLoad
{
[super viewDidLoad];
@ -162,17 +159,14 @@ extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotifi
bmCell.textLabel.text = @(bm->GetName().c_str());
bmCell.imageView.image = [CircleView createCircleImageWith:PINDIAMETER andColor:[ColorPickerView colorForName:@(bm->GetType().c_str())]];
// Get current position and compass "north" direction
double azimut = -1.0;
double lat, lon;
if ([self.locationManager getLat:lat Lon:lon])
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (lastLocation)
{
double north = -1.0;
[self.locationManager getNorthRad:north];
double north = location_helpers::headingToNorthRad([MWMLocationManager lastHeading]);
string distance;
fr.GetDistanceAndAzimut(bm->GetPivot(), lat, lon, north, distance, azimut);
double azimut = -1.0;
fr.GetDistanceAndAzimut(bm->GetPivot(), lastLocation.coordinate.latitude,
lastLocation.coordinate.longitude, north, distance, azimut);
bmCell.detailTextLabel.text = @(distance.c_str());
}
@ -327,8 +321,8 @@ extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotifi
}
}
//******************************************************************
//*********** Location manager callbacks ***************************
#pragma mark - MWMLocationObserver
- (void)onLocationUpdate:(location::GpsInfo const &)info
{
// Refresh distance
@ -347,7 +341,7 @@ extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotifi
m2::PointD const center = bm->GetPivot();
double const metres = ms::DistanceOnEarth(info.m_latitude, info.m_longitude,
MercatorBounds::YToLat(center.y), MercatorBounds::XToLon(center.x));
cell.detailTextLabel.text = [LocationManager formattedDistance:metres];
cell.detailTextLabel.text = location_helpers::formattedDistance(metres);
}
}
}];
@ -359,7 +353,7 @@ extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotifi
- (void)viewWillAppear:(BOOL)animated
{
[self.locationManager start:self];
[MWMLocationManager addObserver:self];
// Display Edit button only if table is not empty
BookmarkCategory * cat = GetFramework().GetBmCategory(m_categoryIndex);
@ -386,7 +380,8 @@ extern NSString * const kBookmarksChangedNotification = @"BookmarksChangedNotifi
- (void)viewWillDisappear:(BOOL)animated
{
[self.locationManager stop:self];
[MWMLocationManager removeObserver:self];
// Save possibly edited set name
UITableViewCell * cell = [self.tableView cellForRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:0]];
if ([cell isKindOfClass:[MWMBookmarkNameCell class]])

View file

@ -0,0 +1,7 @@
#include "geometry/point2d.hpp"
@interface CLLocation (Mercator)
- (m2::PointD)mercator;
@end

View file

@ -0,0 +1,8 @@
#import "CLLocation+Mercator.h"
#import "MWMLocationHelpers.h"
@implementation CLLocation (Mercator)
- (m2::PointD)mercator { return location_helpers::ToMercator(self.coordinate); }
@end

View file

@ -1,4 +1,3 @@
#import "LocationManager.h"
#import "MapsAppDelegate.h"
#import "MapViewController.h"
#import "MWMAlertViewController.h"

View file

@ -10,6 +10,7 @@
#import "MWMButton.h"
#import "MWMFrameworkListener.h"
#import "MWMFrameworkObservers.h"
#import "MWMLocationManager.h"
#import "MWMMapViewControlsManager.h"
#import "MWMSearchManager.h"
#import "SettingsAndMoreVC.h"
@ -293,8 +294,8 @@ typedef NS_ENUM(NSUInteger, MWMBottomMenuViewCell)
{
[Statistics logEvent:kStatMenu withParameters:@{kStatButton : kStatShare}];
[Alohalytics logEvent:kAlohalyticsTapEventKey withValue:@"share@"];
CLLocation * location = [MapsAppDelegate theApp].locationManager.lastLocation;
if (!location)
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation)
{
[[[UIAlertView alloc] initWithTitle:L(@"unknown_current_position")
message:nil
@ -303,7 +304,7 @@ typedef NS_ENUM(NSUInteger, MWMBottomMenuViewCell)
otherButtonTitles:nil] show];
return;
}
CLLocationCoordinate2D const coord = location.coordinate;
CLLocationCoordinate2D const coord = lastLocation.coordinate;
NSIndexPath * cellIndex = [NSIndexPath indexPathForItem:MWMBottomMenuViewCellShare inSection:0];
MWMBottomMenuCollectionViewCell * cell =
(MWMBottomMenuCollectionViewCell *)[self.additionalButtons cellForItemAtIndexPath:cellIndex];

View file

@ -10,6 +10,7 @@
#import "MWMButton.h"
#import "MWMFrameworkListener.h"
#import "MWMFrameworkObservers.h"
#import "MWMLocationHelpers.h"
#import "MWMMapViewControlsManager.h"
#import "MWMObjectsCategorySelectorController.h"
#import "MWMPlacePageEntity.h"
@ -90,9 +91,9 @@ extern NSString * const kAlohalyticsTapEventKey;
}
else
{
LocationManager * m = MapsAppDelegate.theApp.locationManager;
self.routeSource = m.lastLocationIsValid ? MWMRoutePoint(m.lastLocation.mercator)
: MWMRoutePoint::MWMRoutePointZero();
CLLocation * lastLocation = [MWMLocationManager lastLocation];
self.routeSource =
lastLocation ? MWMRoutePoint(lastLocation.mercator) : MWMRoutePoint::MWMRoutePointZero();
}
self.routeDestination = MWMRoutePoint::MWMRoutePointZero();
}
@ -383,8 +384,11 @@ extern NSString * const kAlohalyticsTapEventKey;
- (void)restoreRouteTo:(m2::PointD const &)to
{
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation)
return;
auto & f = GetFramework();
m2::PointD const myPosition = [MapsAppDelegate theApp].locationManager.lastLocation.mercator;
m2::PointD const myPosition = lastLocation.mercator;
f.SetRouter(f.GetBestRouter(myPosition, to));
self.routeSource = MWMRoutePoint(myPosition);
self.routeDestination = {to, @"Destination"};
@ -498,8 +502,8 @@ extern NSString * const kAlohalyticsTapEventKey;
if (!self.isPossibleToBuildRoute)
return;
LocationManager * locMgr = [MapsAppDelegate theApp].locationManager;
if (!locMgr.lastLocation && self.routeSource.IsMyPosition())
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation && self.routeSource.IsMyPosition())
{
MWMAlertViewController * alert =
[[MWMAlertViewController alloc] initWithViewController:self.ownerController];
@ -507,14 +511,14 @@ extern NSString * const kAlohalyticsTapEventKey;
return;
}
m2::PointD const locationPoint = locMgr.lastLocation.mercator;
m2::PointD const locationPoint = lastLocation.mercator;
if (self.routeSource.IsMyPosition())
{
[Statistics
logEvent:kStatPointToPoint
withParameters:@{kStatAction : kStatBuildRoute, kStatValue : kStatFromMyPosition}];
self.routeSource = MWMRoutePoint(locationPoint);
[locMgr start:self.navigationManager];
[MWMLocationManager addObserver:self.navigationManager];
}
else if (self.routeDestination.IsMyPosition())
{
@ -567,9 +571,9 @@ extern NSString * const kAlohalyticsTapEventKey;
if (!isSourceMyPosition)
{
MWMAlertViewController * controller = [[MWMAlertViewController alloc] initWithViewController:self.ownerController];
LocationManager * manager = MapsAppDelegate.theApp.locationManager;
BOOL const needToRebuild = manager.lastLocationIsValid && !manager.isLocationPendingOrNoPosition && !isDestinationMyPosition;
m2::PointD const locationPoint = manager.lastLocation.mercator;
CLLocation * lastLocation = [MWMLocationManager lastLocation];
BOOL const needToRebuild = lastLocation && !location_helpers::isMyPositionPendingOrNoPosition() && !isDestinationMyPosition;
m2::PointD const locationPoint = lastLocation.mercator;
[controller presentPoint2PointAlertWithOkBlock:^
{
self.routeSource = MWMRoutePoint(locationPoint);
@ -596,7 +600,7 @@ extern NSString * const kAlohalyticsTapEventKey;
- (void)didCancelRouting
{
[Statistics logEvent:kStatEventName(kStatPointToPoint, kStatClose)];
[[MapsAppDelegate theApp].locationManager stop:self.navigationManager];
[MWMLocationManager removeObserver:self.navigationManager];
self.navigationManager.state = MWMNavigationDashboardStateHidden;
self.disableStandbyOnRouteFollowing = NO;
[MapsAppDelegate theApp].routingPlaneMode = MWMRoutingPlaneModeNone;
@ -634,9 +638,9 @@ extern NSString * const kAlohalyticsTapEventKey;
- (void)resetRoutingPoint
{
LocationManager * m = MapsAppDelegate.theApp.locationManager;
self.routeSource = m.lastLocationIsValid ? MWMRoutePoint(m.lastLocation.mercator) :
MWMRoutePoint::MWMRoutePointZero();
CLLocation * lastLocation = [MWMLocationManager lastLocation];
self.routeSource =
lastLocation ? MWMRoutePoint(lastLocation.mercator) : MWMRoutePoint::MWMRoutePointZero();
self.routeDestination = MWMRoutePoint::MWMRoutePointZero();
}

View file

@ -1,7 +1,7 @@
#import "LocationManager.h"
#import "MapsAppDelegate.h"
#import "MWMConsole.h"
#import "MWMFrameworkListener.h"
#import "MWMLocationManager.h"
#import "MWMNoMapsViewController.h"
#import "MWMRoutingProtocol.h"
#import "MWMSearchManager.h"
@ -173,7 +173,7 @@ extern NSString * const kSearchStateKey = @"SearchStateKey";
- (void)tapMyPositionFromHistory
{
MapsAppDelegate * a = MapsAppDelegate.theApp;
MWMRoutePoint const p = MWMRoutePoint::MWMRoutePoint(a.locationManager.lastLocation.mercator);
MWMRoutePoint const p = MWMRoutePoint::MWMRoutePoint([MWMLocationManager lastLocation].mercator);
if (a.routingPlaneMode == MWMRoutingPlaneModeSearchSource)
[self.delegate buildRouteFrom:p];
else if (a.routingPlaneMode == MWMRoutingPlaneModeSearchDestination)

View file

@ -1,7 +1,7 @@
#import "Common.h"
#import "LocationManager.h"
#import "Macros.h"
#import "MapsAppDelegate.h"
#import "MWMLocationManager.h"
#import "MWMSearchHistoryClearCell.h"
#import "MWMSearchHistoryManager.h"
#import "MWMSearchHistoryMyPositionCell.h"
@ -27,9 +27,9 @@ static NSString * const kMyPositionCellIdentifier = @"MWMSearchHistoryMyPosition
- (BOOL)isRouteSearchMode
{
MWMRoutingPlaneMode const m = MapsAppDelegate.theApp.routingPlaneMode;
return (m == MWMRoutingPlaneModeSearchSource ||
m == MWMRoutingPlaneModeSearchDestination) &&
MapsAppDelegate.theApp.locationManager.lastLocationIsValid;
CLLocation * lastLocation = [MWMLocationManager lastLocation];
return lastLocation &&
(m == MWMRoutingPlaneModeSearchSource || m == MWMRoutingPlaneModeSearchDestination);
}
- (void)attachCell:(MWMSearchTabbedCollectionViewCell *)cell

View file

@ -1,6 +1,6 @@
#import "Common.h"
#import "LocationManager.h"
#import "MapsAppDelegate.h"
#import "MWMLocationManager.h"
#import "MWMSearchCommonCell.h"
#import "UIColor+MapsMeColor.h"
#import "UIFont+MapsMeFonts.h"
@ -57,12 +57,10 @@
if (result.HasPoint())
{
string distanceStr;
double lat, lon;
LocationManager * locationManager = MapsAppDelegate.theApp.locationManager;
if ([locationManager getLat:lat Lon:lon])
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (lastLocation)
{
m2::PointD const mercLoc = MercatorBounds::FromLatLon(lat, lon);
double const dist = MercatorBounds::DistanceOnEarth(mercLoc, result.GetFeatureCenter());
double const dist = MercatorBounds::DistanceOnEarth(lastLocation.mercator, result.GetFeatureCenter());
MeasurementUtils::FormatDistance(dist, distanceStr);
}
self.distanceLabel.text = @(distanceStr.c_str());

View file

@ -1,6 +1,6 @@
#import "LocationManager.h"
#import "Macros.h"
#import "MapsAppDelegate.h"
#import "MWMLocationManager.h"
#import "MWMSearchCommonCell.h"
#import "MWMSearchShowOnMapCell.h"
#import "MWMSearchSuggestionCell.h"
@ -35,8 +35,7 @@ NSString * identifierForType(MWMSearchTableCellType type)
}
}
@interface MWMSearchTableViewController () <UITableViewDataSource, UITableViewDelegate,
LocationObserver>
@interface MWMSearchTableViewController () <UITableViewDataSource, UITableViewDelegate, MWMLocationObserver>
@property (weak, nonatomic) IBOutlet UITableView * tableView;
@ -292,7 +291,7 @@ forRowAtIndexPath:(NSIndexPath *)indexPath
[self.tableView reloadData];
}
#pragma mark - LocationObserver
#pragma mark - MWMLocationObserver
- (void)onLocationUpdate:(location::GpsInfo const &)info
{
@ -360,9 +359,9 @@ forRowAtIndexPath:(NSIndexPath *)indexPath
return;
_watchLocationUpdates = watchLocationUpdates;
if (watchLocationUpdates)
[[MapsAppDelegate theApp].locationManager start:self];
[MWMLocationManager addObserver:self];
else
[[MapsAppDelegate theApp].locationManager stop:self];
[MWMLocationManager removeObserver:self];
}
@synthesize searchOnMap = _searchOnMap;

View file

@ -1,12 +1,19 @@
#import "LocationManager.h"
#import "MapsAppDelegate.h"
#import "MapViewController.h"
#import "MWMFirstLaunchController.h"
#import "MWMLocationManager.h"
#import "MWMPageController.h"
#include "Framework.h"
@interface MWMFirstLaunchController () <LocationObserver>
@interface MWMLocationManager ()
@property (nonatomic) BOOL started;
+ (MWMLocationManager *)manager;
@end
@interface MWMFirstLaunchController ()
@property (weak, nonatomic) IBOutlet UIView * containerView;
@property (weak, nonatomic) IBOutlet UIImageView * image;
@ -22,8 +29,6 @@
@property (weak, nonatomic) IBOutlet NSLayoutConstraint * titleTopOffset;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint * titleImageOffset;
@property (nonatomic) BOOL locationError;
@end
namespace
@ -50,12 +55,11 @@ void zoomToCurrentPosition()
{
auto & f = GetFramework();
f.SwitchMyPositionNextMode();
LocationManager * locationManager = MapsAppDelegate.theApp.locationManager;
if (![locationManager lastLocationIsValid])
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation)
return;
m2::PointD const centerPt = locationManager.lastLocation.mercator;
int const zoom = 13;
f.SetViewportCenter(centerPt, zoom);
f.SetViewportCenter(lastLocation.mercator, zoom);
}
NSInteger constexpr kRequestLocationPage = 2;
@ -117,16 +121,6 @@ NSArray<TMWMWelcomeConfigBlock> * pagesConfigBlocks = @[
return pagesConfigBlocks;
}
- (void)viewDidLoad
{
[super viewDidLoad];
if (self.pageIndex == kRequestLocationPage)
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(appWillEnterForeground:)
name:UIApplicationWillEnterForegroundNotification
object:nil];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
@ -136,24 +130,9 @@ NSArray<TMWMWelcomeConfigBlock> * pagesConfigBlocks = @[
requestNotifications();
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if (self.locationError)
[MapsAppDelegate.theApp.locationManager reset];
}
- (void)dealloc
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)requestLocation
{
MapsAppDelegate * app = MapsAppDelegate.theApp;
LocationManager * lm = app.locationManager;
[lm onForeground];
[lm start:self];
[MWMLocationManager manager].started = YES;
}
- (void)close
@ -162,23 +141,6 @@ NSArray<TMWMWelcomeConfigBlock> * pagesConfigBlocks = @[
zoomToCurrentPosition();
}
- (void)appWillEnterForeground:(NSNotification *)notification
{
[self requestLocation];
}
#pragma mark - LocationManager Callbacks
- (void)onLocationUpdate:(location::GpsInfo const &)info
{
}
- (void)onLocationError:(location::TLocationError)errorCode
{
if (errorCode == location::EDenied)
self.locationError = YES;
}
#pragma mark - Properties
- (void)setSize:(CGSize)size

View file

@ -1,6 +1,6 @@
#import "Common.h"
#import "LocationManager.h"
#import "MapsAppDelegate.h"
#import "MWMLocationManager.h"
#import "MWMNavigationDashboardEntity.h"
#include "Framework.h"
@ -23,11 +23,12 @@ using namespace routing::turns;
_targetUnits = @(info.m_targetUnitsSuffix.c_str());
_progress = info.m_completionPercent;
auto & f = GetFramework();
if (f.GetRouter() == routing::RouterType::Pedestrian)
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (lastLocation && f.GetRouter() == routing::RouterType::Pedestrian)
{
_isPedestrian = YES;
string distance;
CLLocationCoordinate2D const & coordinate ([MapsAppDelegate theApp].locationManager.lastLocation.coordinate);
CLLocationCoordinate2D const & coordinate = lastLocation.coordinate;
ms::LatLon const & directionPos = info.m_pedestrianDirectionPos;
//TODO: Not the best solution, but this solution is temporary and will be replaced in future
MeasurementUtils::FormatDistance(ms::DistanceOnEarth(coordinate.latitude, coordinate.longitude,

View file

@ -1,5 +1,5 @@
#import "LocationManager.h"
#import "MWMCircularProgress.h"
#import "MWMLocationManager.h"
#import "MWMNavigationViewProtocol.h"
#import "MWMRoutePreview.h"
@ -30,7 +30,7 @@ typedef NS_ENUM(NSUInteger, MWMNavigationDashboardState)
@class MWMNavigationDashboardEntity;
@interface MWMNavigationDashboardManager : NSObject <LocationObserver>
@interface MWMNavigationDashboardManager : NSObject <MWMLocationObserver>
@property (nonatomic, readonly) MWMNavigationDashboardEntity * entity;
@property (weak, nonatomic, readonly) MWMRoutePreview * routePreview;

View file

@ -2,6 +2,7 @@
#import "Macros.h"
#import "MapsAppDelegate.h"
#import "MWMLanesPanel.h"
#import "MWMLocationHelpers.h"
#import "MWMNavigationDashboard.h"
#import "MWMNavigationDashboardEntity.h"
#import "MWMNavigationDashboardManager.h"
@ -432,23 +433,16 @@ extern NSString * const kTTSStatusWasChangedNotification;
}
}
#pragma mark - LocationObserver
#pragma mark - MWMLocationObserver
- (void)onLocationUpdate:(const location::GpsInfo &)info
{
// We don't need information about location update in this class,
// but in LocationObserver protocol this method is required
// since we don't want runtime overhead for introspection.
}
- (void)onCompassUpdate:(location::CompassInfo const &)info
- (void)onHeadingUpdate:(location::CompassInfo const &)info
{
auto & f = GetFramework();
if (f.GetRouter() != routing::RouterType::Pedestrian)
return;
CLLocation * location = [MapsAppDelegate theApp].locationManager.lastLocation;
if (!location)
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation)
return;
location::FollowingInfo res;
@ -456,9 +450,10 @@ extern NSString * const kTTSStatusWasChangedNotification;
if (!res.IsValid())
return;
CGFloat const angle = ang::AngleTo(location.mercator,
ToMercator(res.m_pedestrianDirectionPos)) + info.m_bearing;
CGAffineTransform const transform (CGAffineTransformMakeRotation(M_PI_2 - angle));
CGFloat const angle = ang::AngleTo(lastLocation.mercator,
location_helpers::ToMercator(res.m_pedestrianDirectionPos)) +
info.m_bearing;
CGAffineTransform const transform(CGAffineTransformMakeRotation(M_PI_2 - angle));
self.navigationDashboardPortrait.direction.transform = transform;
self.navigationDashboardLandscape.direction.transform = transform;
}

View file

@ -1,7 +1,6 @@
#import "Common.h"
#import "EAGLView.h"
#import "MapsAppDelegate.h"
#import "LocationManager.h"
#import "MWMDirectionView.h"
#import "../Platform/opengl/iosOGLContextFactory.h"
@ -64,7 +63,7 @@ double getExactDPI(double contentScaleFactor)
{
NSLog(@"EAGLView initWithCoder Started");
self = [super initWithCoder:coder];
if (self && !MapsAppDelegate.theApp.isDaemonMode)
if (self)
[self initialize];
NSLog(@"EAGLView initWithCoder Ended");
@ -74,7 +73,6 @@ double getExactDPI(double contentScaleFactor)
- (void)initialize
{
lastViewSize = CGRectZero;
_widgetsManager = [[MWMMapWidgets alloc] init];
// Setup Layer Properties
CAEAGLLayer * eaglLayer = (CAEAGLLayer *)self.layer;
@ -92,8 +90,6 @@ double getExactDPI(double contentScaleFactor)
- (void)createDrapeEngineWithWidth:(int)width height:(int)height
{
LOG(LINFO, ("EAGLView createDrapeEngine Started"));
if (MapsAppDelegate.theApp.isDaemonMode)
return;
Framework::DrapeCreationParams p;
p.m_surfaceWidth = width;
@ -181,4 +177,11 @@ double getExactDPI(double contentScaleFactor)
m_factory->CastFactory<iosOGLContextFactory>()->setPresentAvailable(available);
}
- (MWMMapWidgets *)widgetsManager
{
if (!_widgetsManager)
_widgetsManager = [[MWMMapWidgets alloc] init];
return _widgetsManager;
}
@end

View file

@ -1,6 +1,6 @@
#import "CLLocation+Mercator.h"
#import "Common.h"
#import "LocalNotificationManager.h"
#import "LocationManager.h"
#import "MapsAppDelegate.h"
#import "MapViewController.h"
#import "MWMStorage.h"

View file

@ -0,0 +1,110 @@
#import "MWMLocationManager.h"
#include "platform/location.hpp"
#include "platform/measurement_utils.hpp"
#include "geometry/mercator.hpp"
namespace location_helpers
{
static inline char const * getSpeedSymbol(CLLocationSpeed const & metersPerSecond)
{
// 0-1 m/s
static char const * turtle = "\xF0\x9F\x90\xA2 ";
// 1-2 m/s
static char const * pedestrian = "\xF0\x9F\x9A\xB6 ";
// 2-5 m/s
static char const * tractor = "\xF0\x9F\x9A\x9C ";
// 5-10 m/s
static char const * bicycle = "\xF0\x9F\x9A\xB2 ";
// 10-36 m/s
static char const * car = "\xF0\x9F\x9A\x97 ";
// 36-120 m/s
static char const * train = "\xF0\x9F\x9A\x85 ";
// 120-278 m/s
static char const * airplane = "\xE2\x9C\x88\xEF\xB8\x8F ";
// 278+
static char const * rocket = "\xF0\x9F\x9A\x80 ";
if (metersPerSecond <= 1.)
return turtle;
else if (metersPerSecond <= 2.)
return pedestrian;
else if (metersPerSecond <= 5.)
return tractor;
else if (metersPerSecond <= 10.)
return bicycle;
else if (metersPerSecond <= 36.)
return car;
else if (metersPerSecond <= 120.)
return train;
else if (metersPerSecond <= 278.)
return airplane;
else
return rocket;
}
static inline NSString * formattedSpeedAndAltitude(CLLocation * location)
{
if (!location)
return nil;
string result;
if (location.altitude)
result = "\xE2\x96\xB2 " /* this is simple mountain symbol */ +
MeasurementUtils::FormatAltitude(location.altitude);
// Speed is actual only for just received location
if (location.speed > 0. && [location.timestamp timeIntervalSinceNow] >= -2.0)
{
if (!result.empty())
result += " ";
result += getSpeedSymbol(location.speed) + MeasurementUtils::FormatSpeed(location.speed);
}
return result.empty() ? nil : @(result.c_str());
}
static inline NSString * formattedDistance(double const & meters)
{
if (meters < 0.)
return nil;
string s;
MeasurementUtils::FormatDistance(meters, s);
return @(s.c_str());
}
static inline BOOL isMyPositionPendingOrNoPosition()
{
location::EMyPositionMode mode;
if (!settings::Get(settings::kLocationStateMode, mode))
return true;
return mode == location::EMyPositionMode::PendingPosition ||
mode == location::EMyPositionMode::NotFollowNoPosition;
}
static inline BOOL isLocationProhibited()
{
auto const status = [MWMLocationManager lastLocationStatus];
return status == location::TLocationError::EDenied || status == location::TLocationError::EGPSIsOff;
}
static inline double headingToNorthRad(CLHeading * heading)
{
double north = -1.0;
if (heading)
{
north = (heading.trueHeading < 0) ? heading.magneticHeading : heading.trueHeading;
north = my::DegToRad(north);
}
return north;
}
static inline ms::LatLon ToLatLon(m2::PointD const & p) { return MercatorBounds::ToLatLon(p); }
static inline m2::PointD ToMercator(CLLocationCoordinate2D const & l)
{
return MercatorBounds::FromLatLon(l.latitude, l.longitude);
}
static inline m2::PointD ToMercator(ms::LatLon const & l) { return MercatorBounds::FromLatLon(l); }
} // namespace MWMLocationHelpers

View file

@ -0,0 +1,35 @@
#import "CLLocation+Mercator.h"
#include "platform/location.hpp"
@protocol MWMLocationObserver <NSObject>
@optional
- (void)onHeadingUpdate:(location::CompassInfo const &)compassinfo;
- (void)onLocationUpdate:(location::GpsInfo const &)gpsInfo;
- (void)onLocationError:(location::TLocationError)locationError;
@end
@interface MWMLocationManager : NSObject
+ (void)addObserver:(id<MWMLocationObserver>)observer;
+ (void)removeObserver:(id<MWMLocationObserver>)observer;
+ (void)setMyPositionMode:(location::EMyPositionMode)mode;
+ (CLLocation *)lastLocation;
+ (location::TLocationError)lastLocationStatus;
+ (CLHeading *)lastHeading;
+ (void)applicationDidBecomeActive;
+ (void)applicationWillResignActive;
- (instancetype)init __attribute__((unavailable("call +manager instead")));
- (instancetype)copy __attribute__((unavailable("call +manager instead")));
- (instancetype)copyWithZone:(NSZone *)zone __attribute__((unavailable("call +manager instead")));
+ (instancetype)alloc __attribute__((unavailable("call +manager instead")));
+ (instancetype)allocWithZone:(struct _NSZone *)zone __attribute__((unavailable("call +manager instead")));
+ (instancetype)new __attribute__((unavailable("call +manager instead")));
@end

View file

@ -0,0 +1,555 @@
#import "Common.h"
#import "MapsAppDelegate.h"
#import "MWMAlertViewController.h"
#import "MWMController.h"
#import "MWMLocationManager.h"
#import "MWMLocationPredictor.h"
#import "Statistics.h"
#import "3party/Alohalytics/src/alohalytics_objc.h"
#include "Framework.h"
#include "map/gps_tracker.hpp"
#include "std/map.hpp"
namespace
{
using TObserver = id<MWMLocationObserver>;
using TObservers = NSHashTable<__kindof TObserver>;
void runAsyncOnMainQueue(dispatch_block_t block)
{
dispatch_async(dispatch_get_main_queue(), block);
}
location::GpsInfo gpsInfoFromLocation(CLLocation * l)
{
location::GpsInfo info;
info.m_source = location::EAppleNative;
info.m_latitude = l.coordinate.latitude;
info.m_longitude = l.coordinate.longitude;
info.m_timestamp = l.timestamp.timeIntervalSince1970;
if (l.horizontalAccuracy >= 0.0)
info.m_horizontalAccuracy = l.horizontalAccuracy;
if (l.verticalAccuracy >= 0.0)
{
info.m_verticalAccuracy = l.verticalAccuracy;
info.m_altitude = l.altitude;
}
if (l.course >= 0.0)
info.m_bearing = l.course;
if (l.speed >= 0.0)
info.m_speed = l.speed;
return info;
}
location::CompassInfo compassInfoFromHeading(CLHeading * h)
{
location::CompassInfo info;
if (h.trueHeading >= 0.0)
info.m_bearing = my::DegToRad(h.trueHeading);
else if (h.headingAccuracy >= 0.0)
info.m_bearing = my::DegToRad(h.magneticHeading);
return info;
}
enum class GeoMode
{
Pending,
InPosition,
NotInPosition,
FollowAndRotate,
VehicleRouting,
PedestrianRouting,
BicycleRouting
};
struct DesiredAccuracy
{
CLLocationAccuracy charging;
CLLocationAccuracy battery;
};
struct GeoModeSettings
{
CLLocationDistance distanceFilter;
DesiredAccuracy accuracy;
};
map<GeoMode, GeoModeSettings> const kGeoSettings{
{GeoMode::Pending,
{.distanceFilter = kCLDistanceFilterNone,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBestForNavigation}}},
{GeoMode::InPosition,
{.distanceFilter = 2,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBest}}},
{GeoMode::NotInPosition,
{.distanceFilter = 5,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBest}}},
{GeoMode::FollowAndRotate,
{.distanceFilter = 2,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBest}}},
{GeoMode::VehicleRouting,
{.distanceFilter = kCLDistanceFilterNone,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBest}}},
{GeoMode::PedestrianRouting,
{.distanceFilter = 2,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBest}}},
{GeoMode::BicycleRouting,
{.distanceFilter = 2,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBest}}}};
BOOL keepRunningInBackground()
{
bool const needGPSForTrackRecorder = GpsTracker::Instance().IsEnabled();
if (needGPSForTrackRecorder)
return YES;
auto const & f = GetFramework();
bool const isRouteBuilt = f.IsRouteBuilt();
bool const isRouteFinished = f.IsRouteFinished();
bool const isRouteRebuildingOnly = f.IsRouteRebuildingOnly();
bool const needGPSForRouting = ((isRouteBuilt || isRouteRebuildingOnly) && !isRouteFinished);
if (needGPSForRouting)
return YES;
return NO;
}
MWMAlertViewController * alertController()
{
UIWindow * window = UIApplication.sharedApplication.delegate.window;
UIViewController * rootViewController = window.rootViewController;
ASSERT([rootViewController isKindOfClass:[UINavigationController class]], ());
UINavigationController * navigationController = static_cast<UINavigationController *>(rootViewController);
UIViewController * topViewController = navigationController.topViewController;
ASSERT([topViewController conformsToProtocol:@protocol(MWMController)], ());
UIViewController<MWMController> * mwmController = static_cast<UIViewController<MWMController> *>(topViewController);
return mwmController.alertController;
}
void sendInfoToFramework(dispatch_block_t block)
{
MapsAppDelegate * delegate = static_cast<MapsAppDelegate *>(UIApplication.sharedApplication.delegate);
if (delegate.isDrapeEngineCreated)
{
block();
}
else
{
runAsyncOnMainQueue(^
{
sendInfoToFramework(block);
});
}
}
} // namespace
@interface MWMLocationManager () <CLLocationManagerDelegate>
@property (nonatomic) BOOL started;
@property (nonatomic) CLLocationManager * locationManager;
@property (nonatomic) GeoMode geoMode;
@property (nonatomic) CLHeading * lastHeadingInfo;
@property (nonatomic) CLLocation * lastLocationInfo;
@property (nonatomic) location::TLocationError lastLocationStatus;
@property (nonatomic) MWMLocationPredictor * predictor;
@property (nonatomic) TObservers * observers;
@end
@implementation MWMLocationManager
#pragma mark - Init
+ (MWMLocationManager *)manager
{
static MWMLocationManager * manager;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ manager = [[super alloc] initManager]; });
return manager;
}
- (instancetype)initManager
{
self = [super init];
if (self)
_observers = [TObservers weakObjectsHashTable];
return self;
}
#pragma mark - Add/Remove Observers
+ (void)addObserver:(TObserver)observer
{
runAsyncOnMainQueue(^
{
MWMLocationManager * manager = [MWMLocationManager manager];
[manager.observers addObject:observer];
[manager processLocationUpdate:manager.lastLocationInfo];
});
}
+ (void)removeObserver:(TObserver)observer
{
runAsyncOnMainQueue(^
{
[[MWMLocationManager manager].observers removeObject:observer];
});
}
#pragma mark - App Life Cycle
+ (void)applicationDidBecomeActive
{
if (![Alohalytics isFirstSession])
[MWMLocationManager manager].started = YES;
}
+ (void)applicationWillResignActive
{
BOOL const keepRunning = keepRunningInBackground();
MWMLocationManager * manager = [MWMLocationManager manager];
CLLocationManager * locationManager = manager.locationManager;
if ([locationManager respondsToSelector:@selector(setAllowsBackgroundLocationUpdates:)])
[locationManager setAllowsBackgroundLocationUpdates:keepRunning];
manager.started = keepRunning;
}
#pragma mark - Getters
+ (CLLocation *)lastLocation
{
MWMLocationManager * manager = [MWMLocationManager manager];
if (!manager.started || !manager.lastLocationInfo ||
manager.lastLocationInfo.horizontalAccuracy < 0 ||
manager.lastLocationStatus != location::TLocationError::ENoError)
return nil;
return manager.lastLocationInfo;
}
+ (location::TLocationError)lastLocationStatus
{
return [MWMLocationManager manager].lastLocationStatus;
}
+ (CLHeading *)lastHeading
{
MWMLocationManager * manager = [MWMLocationManager manager];
if (!manager.started || !manager.lastHeadingInfo || manager.lastHeadingInfo.headingAccuracy < 0)
return nil;
return manager.lastHeadingInfo;
}
#pragma mark - Observer notifications
- (void)processLocationStatus:(location::TLocationError)locationError
{
// if (self.lastLocationStatus == locationError)
// return;
self.lastLocationStatus = locationError;
sendInfoToFramework(^
{
if (self.lastLocationStatus != location::TLocationError::ENoError)
GetFramework().OnLocationError(self.lastLocationStatus);
});
for (TObserver observer in self.observers)
{
if ([observer respondsToSelector:@selector(onLocationError:)])
[observer onLocationError:self.lastLocationStatus];
}
}
- (void)processHeadingUpdate:(CLHeading *)headingInfo
{
self.lastHeadingInfo = headingInfo;
sendInfoToFramework(^
{
GetFramework().OnCompassUpdate(compassInfoFromHeading(self.lastHeadingInfo));
});
location::CompassInfo const compassInfo = compassInfoFromHeading(headingInfo);
for (TObserver observer in self.observers)
{
if ([observer respondsToSelector:@selector(onHeadingUpdate:)])
[observer onHeadingUpdate:compassInfo];
}
}
- (void)processLocationUpdate:(CLLocation *)locationInfo
{
if (!locationInfo)
return;
location::GpsInfo const gpsInfo = gpsInfoFromLocation(locationInfo);
[self onLocationUpdate:gpsInfo];
if (self.lastLocationInfo == locationInfo)
return;
self.lastLocationInfo = locationInfo;
self.lastLocationStatus = location::TLocationError::ENoError;
[self.predictor reset:gpsInfo];
}
- (void)onLocationUpdate:(location::GpsInfo const &)gpsInfo
{
GpsTracker::Instance().OnLocationUpdated(gpsInfo);
sendInfoToFramework([gpsInfo]
{
GetFramework().OnLocationUpdate(gpsInfo);
});
for (TObserver observer in self.observers)
{
if ([observer respondsToSelector:@selector(onLocationUpdate:)])
[observer onLocationUpdate:gpsInfo];
}
}
#pragma mark - Location Status
- (void)setLastLocationStatus:(location::TLocationError)lastLocationStatus
{
_lastLocationStatus = lastLocationStatus;
switch (lastLocationStatus)
{
case location::ENoError:
break;
case location::ENotSupported:
[alertController() presentLocationServiceNotSupportedAlert];
break;
case location::EDenied:
[alertController() presentLocationAlert];
break;
case location::EGPSIsOff:
// iOS shows its own alert.
break;
}
}
#pragma mark - My Position
+ (void)setMyPositionMode:(location::EMyPositionMode)mode
{
MWMLocationManager * manager = [MWMLocationManager manager];
[manager.predictor setMyPositionMode:mode];
[manager processLocationStatus:manager.lastLocationStatus];
auto const & f = GetFramework();
if (f.IsRoutingActive())
{
switch (f.GetRouter())
{
case routing::RouterType::Vehicle:
manager.geoMode = GeoMode::VehicleRouting;
break;
case routing::RouterType::Pedestrian:
manager.geoMode = GeoMode::PedestrianRouting;
break;
case routing::RouterType::Bicycle:
manager.geoMode = GeoMode::BicycleRouting;
break;
}
}
else
{
switch (mode)
{
case location::EMyPositionMode::PendingPosition:
manager.geoMode = GeoMode::Pending;
break;
case location::EMyPositionMode::NotFollowNoPosition:
case location::EMyPositionMode::NotFollow:
manager.geoMode = GeoMode::NotInPosition;
break;
case location::EMyPositionMode::Follow:
manager.geoMode = GeoMode::InPosition;
break;
case location::EMyPositionMode::FollowAndRotate:
manager.geoMode = GeoMode::FollowAndRotate;
break;
}
}
}
#pragma mark - Prediction
- (MWMLocationPredictor *)predictor
{
if (!_predictor)
{
__weak MWMLocationManager * weakSelf = self;
_predictor = [[MWMLocationPredictor alloc] initWithOnPredictionBlock:^(location::GpsInfo const & gpsInfo)
{
[weakSelf onLocationUpdate:gpsInfo];
}];
}
return _predictor;
}
#pragma mark - Device notifications
- (void)orientationChanged
{
self.locationManager.headingOrientation = (CLDeviceOrientation)[UIDevice currentDevice].orientation;
}
- (void)batteryStateChangedNotification:(NSNotification *)notification
{
[self refreshGeoModeSettings];
}
#pragma mark - Location manager
- (void)setGeoMode:(GeoMode)geoMode
{
if (_geoMode == geoMode)
return;
_geoMode = geoMode;
CLLocationManager * locationManager = self.locationManager;
switch (geoMode)
{
case GeoMode::Pending:
case GeoMode::InPosition:
case GeoMode::NotInPosition:
case GeoMode::FollowAndRotate:
locationManager.activityType = CLActivityTypeOther;
break;
case GeoMode::VehicleRouting:
locationManager.activityType = CLActivityTypeAutomotiveNavigation;
break;
case GeoMode::PedestrianRouting:
case GeoMode::BicycleRouting:
locationManager.activityType = CLActivityTypeFitness;
break;
}
[self refreshGeoModeSettings];
}
- (void)refreshGeoModeSettings
{
UIDeviceBatteryState const state = [UIDevice currentDevice].batteryState;
BOOL const isCharging = (state == UIDeviceBatteryStateCharging || state == UIDeviceBatteryStateFull);
GeoModeSettings const settings = kGeoSettings.at(self.geoMode);
CLLocationManager * locationManager = self.locationManager;
locationManager.desiredAccuracy = isCharging ? settings.accuracy.charging : settings.accuracy.battery;
locationManager.distanceFilter = settings.distanceFilter;
}
- (CLLocationManager *)locationManager
{
if (!_locationManager)
{
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
[self refreshGeoModeSettings];
_locationManager.pausesLocationUpdatesAutomatically = YES;
_locationManager.headingFilter = 3.0;
}
return _locationManager;
}
#pragma mark - CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)heading
{
[self processHeadingUpdate:heading];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
CLLocation * location = locations.lastObject;
// According to documentation, lat and lon are valid only if horizontalAccuracy is non-negative.
// So we filter out such events completely.
if (location.horizontalAccuracy < 0.)
return;
[self processLocationUpdate:location];
[[Statistics instance] logLocation:location];
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
if (self.lastLocationStatus == location::TLocationError::ENoError && error.code == kCLErrorDenied)
[self processLocationStatus:location::EDenied];
}
#pragma mark - Start / Stop
- (void)setStarted:(BOOL)started
{
if (_started == started)
return;
UIDevice * device = [UIDevice currentDevice];
NSNotificationCenter * notificationCenter = [NSNotificationCenter defaultCenter];
if (started)
{
_started = [self start];
device.batteryMonitoringEnabled = YES;
[notificationCenter addObserver:self selector:@selector(orientationChanged) name:UIDeviceOrientationDidChangeNotification object:nil];
[notificationCenter addObserver:self selector:@selector(batteryStateChangedNotification:) name:UIDeviceBatteryStateDidChangeNotification object:nil];
}
else
{
_started = NO;
[self stop];
device.batteryMonitoringEnabled = NO;
[notificationCenter removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
[notificationCenter removeObserver:self name:UIDeviceBatteryStateDidChangeNotification object:nil];
}
}
- (BOOL)start
{
auto const doStart = ^
{
LOG(LINFO, ("startUpdatingLocation"));
CLLocationManager * locationManager = self.locationManager;
if ([locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)])
[locationManager requestWhenInUseAuthorization];
[locationManager startUpdatingLocation];
if ([CLLocationManager headingAvailable])
[locationManager startUpdatingHeading];
};
if ([CLLocationManager locationServicesEnabled])
{
switch ([CLLocationManager authorizationStatus])
{
case kCLAuthorizationStatusAuthorizedWhenInUse:
case kCLAuthorizationStatusAuthorizedAlways:
case kCLAuthorizationStatusNotDetermined:
doStart();
return YES;
case kCLAuthorizationStatusRestricted:
case kCLAuthorizationStatusDenied:
[self processLocationStatus:location::EDenied];
break;
}
}
else
{
// Call start to make iOS show its alert to request geo service.
doStart();
[self processLocationStatus:location::EGPSIsOff];
}
return NO;
}
- (void)stop
{
LOG(LINFO, ("stopUpdatingLocation"));
CLLocationManager * locationManager = self.locationManager;
[locationManager stopUpdatingLocation];
if ([CLLocationManager headingAvailable])
[locationManager stopUpdatingHeading];
}
@end

View file

@ -0,0 +1,13 @@
#import "MWMTypes.h"
#include "platform/location.hpp"
using TPredictionBlock = void (^)(location::GpsInfo const &);
@interface MWMLocationPredictor : NSObject
- (instancetype)initWithOnPredictionBlock:(TPredictionBlock)onPredictBlock;
- (void)reset:(location::GpsInfo const &)info;
- (void)setMyPositionMode:(location::EMyPositionMode)mode;
@end

View file

@ -0,0 +1,81 @@
#import "MWMLocationPredictor.h"
#include "Framework.h"
namespace
{
NSTimeInterval constexpr kPredictionIntervalInSeconds = 0.5;
NSUInteger constexpr kMaxPredictionCount = 20;
} // namespace
@interface MWMLocationPredictor ()
@property (nonatomic) location::GpsInfo lastLocationInfo;
@property (nonatomic) BOOL isLastLocationInfoValid;
@property (nonatomic) BOOL isLastPositionModeValid;
@property (nonatomic) NSUInteger predictionsCount;
@property (copy, nonatomic) TPredictionBlock onPredictionBlock;
@end
@implementation MWMLocationPredictor
- (instancetype)initWithOnPredictionBlock:(TPredictionBlock)onPredictionBlock
{
self = [super init];
if (self)
_onPredictionBlock = [onPredictionBlock copy];
return self;
}
- (void)setMyPositionMode:(location::EMyPositionMode)mode
{
self.isLastPositionModeValid = (mode == location::FollowAndRotate);
[self restart];
}
- (void)reset:(location::GpsInfo const &)locationInfo
{
self.isLastLocationInfoValid = (locationInfo.HasSpeed() && locationInfo.HasBearing());
if (self.isLastLocationInfoValid)
self.lastLocationInfo = locationInfo;
[self restart];
}
- (BOOL)isActive
{
return self.isLastLocationInfoValid && self.isLastPositionModeValid && self.predictionsCount < kMaxPredictionCount;
}
- (void)restart
{
self.predictionsCount = 0;
[self schedule];
}
- (void)schedule
{
SEL const predict = @selector(predict);
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:predict object:nil];
[self performSelector:predict withObject:nil afterDelay:kPredictionIntervalInSeconds];
}
- (void)predict
{
if (!self.isActive)
return;
self.predictionsCount++;
location::GpsInfo info = self.lastLocationInfo;
info.m_source = location::EPredictor;
info.m_timestamp = [NSDate date].timeIntervalSince1970;
Framework::PredictLocation(info.m_latitude, info.m_longitude, info.m_horizontalAccuracy,
info.m_bearing, info.m_speed,
info.m_timestamp - self.lastLocationInfo.m_timestamp);
self.onPredictionBlock(info);
[self schedule];
}
@end

View file

@ -1,13 +0,0 @@
#pragma once
#import "../Platform/LocationManager.h"
#import <Foundation/Foundation.h>
@interface LocationPredictor : NSObject
-(id)initWithObserver:(NSObject<LocationObserver> *) observer;
-(void)reset:(location::GpsInfo const &) info;
-(void)setMode:(location::EMyPositionMode) mode;
@end

View file

@ -1,106 +0,0 @@
#import "LocationPredictor.h"
#include "Framework.h"
#include "base/timer.hpp"
namespace
{
NSTimeInterval const PREDICTION_INTERVAL = 0.2; // in seconds
int const MAX_PREDICTION_COUNT = 20;
}
@implementation LocationPredictor
{
NSObject<LocationObserver> * m_observer;
NSTimer * m_timer;
location::GpsInfo m_lastGpsInfo;
bool m_gpsInfoIsValid;
int m_connectionSlot;
bool m_generatePredictions;
int m_predictionCount;
}
-(id)initWithObserver:(NSObject<LocationObserver> *)observer
{
if ((self = [super init]))
{
m_observer = observer;
m_timer = nil;
m_gpsInfoIsValid = false;
m_generatePredictions = false;
}
return self;
}
-(void)setMode:(location::EMyPositionMode)mode
{
m_generatePredictions = (mode == location::FollowAndRotate);
if (mode == location::PendingPosition || mode == location::NotFollowNoPosition)
m_gpsInfoIsValid = false;
[self resetTimer];
}
-(void)reset:(location::GpsInfo const &)info
{
if (info.HasSpeed() && info.HasBearing())
{
m_gpsInfoIsValid = true;
m_lastGpsInfo = info;
m_lastGpsInfo.m_timestamp = my::Timer::LocalTime();
m_lastGpsInfo.m_source = location::EPredictor;
}
else
m_gpsInfoIsValid = false;
[self resetTimer];
}
-(bool)isPredict
{
return m_gpsInfoIsValid && m_generatePredictions;
}
-(void)resetTimer
{
m_predictionCount = 0;
if (m_timer != nil)
{
[m_timer invalidate];
m_timer = nil;
}
if ([self isPredict])
{
m_timer = [NSTimer scheduledTimerWithTimeInterval:PREDICTION_INTERVAL
target:self
selector:@selector(timerFired)
userInfo:nil
repeats:YES];
}
}
-(void)timerFired
{
if (![self isPredict])
return;
if (m_predictionCount < MAX_PREDICTION_COUNT)
{
++m_predictionCount;
location::GpsInfo info = m_lastGpsInfo;
info.m_timestamp = my::Timer::LocalTime();
::Framework::PredictLocation(info.m_latitude, info.m_longitude, info.m_horizontalAccuracy, info.m_bearing,
info.m_speed, info.m_timestamp - m_lastGpsInfo.m_timestamp);
[m_observer onLocationUpdate:info];
}
}
@end

View file

@ -1,10 +1,10 @@
#import "Common.h"
#import "LocationManager.h"
#import "MapsAppDelegate.h"
#import "MapViewController.h"
#import "MWMBasePlacePageView.h"
#import "MWMCircularProgress.h"
#import "MWMFrameworkListener.h"
#import "MWMLocationHelpers.h"
#import "MWMPlacePage.h"
#import "MWMPlacePageActionBar.h"
#import "MWMPlacePageBookmarkCell.h"
@ -257,7 +257,7 @@ using namespace storage;
self.bookingRatingLabel.text = entity.bookingRating;
self.bookingView.hidden = !entity.bookingPrice.length && !entity.bookingRating.length;
BOOL const isHeadingAvaible = [CLLocationManager headingAvailable];
BOOL const noLocation = MapsAppDelegate.theApp.locationManager.isLocationPendingOrNoPosition;
BOOL const noLocation = location_helpers::isMyPositionPendingOrNoPosition();
self.distanceLabel.hidden = noLocation || isMyPosition;
BOOL const hideDirection = noLocation || isMyPosition || !isHeadingAvaible;
self.directionArrow.hidden = hideDirection;

View file

@ -1,21 +1,22 @@
#import "Common.h"
#import "LocationManager.h"
#import "MWMAPIBar.h"
#import "MapsAppDelegate.h"
#import "MapViewController.h"
#import "MWMActivityViewController.h"
#import "MWMAPIBar.h"
#import "MWMBasePlacePageView.h"
#import "MWMDirectionView.h"
#import "MWMFrameworkListener.h"
#import "MWMiPadPlacePage.h"
#import "MWMiPhoneLandscapePlacePage.h"
#import "MWMiPhonePortraitPlacePage.h"
#import "MWMLocationHelpers.h"
#import "MWMLocationManager.h"
#import "MWMPlacePage.h"
#import "MWMPlacePageActionBar.h"
#import "MWMPlacePageEntity.h"
#import "MWMPlacePageNavigationBar.h"
#import "MWMPlacePageViewManager.h"
#import "MWMPlacePageViewManagerDelegate.h"
#import "MWMiPadPlacePage.h"
#import "MWMiPhoneLandscapePlacePage.h"
#import "MWMiPhonePortraitPlacePage.h"
#import "MapViewController.h"
#import "MapsAppDelegate.h"
#import "Statistics.h"
#import "3party/Alohalytics/src/alohalytics_objc.h"
@ -27,7 +28,7 @@
extern NSString * const kAlohalyticsTapEventKey;
extern NSString * const kBookmarksChangedNotification;
@interface MWMPlacePageViewManager () <LocationObserver>
@interface MWMPlacePageViewManager () <MWMLocationObserver>
@property (weak, nonatomic) MWMViewController * ownerViewController;
@property (nonatomic, readwrite) MWMPlacePageEntity * entity;
@ -61,14 +62,14 @@ extern NSString * const kBookmarksChangedNotification;
{
[self.delegate placePageDidClose];
[self.placePage dismiss];
[[MapsAppDelegate theApp].locationManager stop:self];
[MWMLocationManager removeObserver:self];
GetFramework().DeactivateMapSelection(false);
self.placePage = nil;
}
- (void)showPlacePage:(place_page::Info const &)info
{
[[MapsAppDelegate theApp].locationManager start:self];
[MWMLocationManager addObserver:self];
self.entity = [[MWMPlacePageEntity alloc] initWithInfo:info];
if (IPAD)
[self setPlacePageForiPad];
@ -111,10 +112,8 @@ extern NSString * const kBookmarksChangedNotification;
- (void)configPlacePage
{
if (self.entity.isMyPosition)
{
BOOL hasSpeed;
self.entity.subtitle = [[MapsAppDelegate theApp].locationManager formattedSpeedAndAltitude:hasSpeed];
}
self.entity.subtitle =
location_helpers::formattedSpeedAndAltitude([MWMLocationManager lastLocation]);
self.placePage.parentViewHeight = self.ownerViewController.view.height;
[self.placePage configure];
self.placePage.topBound = self.topBound;
@ -147,11 +146,9 @@ extern NSString * const kBookmarksChangedNotification;
- (void)updateMyPositionSpeedAndAltitude
{
if (!self.entity.isMyPosition)
return;
BOOL hasSpeed = NO;
[self.placePage updateMyPositionStatus:[[MapsAppDelegate theApp].locationManager
formattedSpeedAndAltitude:hasSpeed]];
if (self.entity.isMyPosition)
[self.placePage updateMyPositionStatus:location_helpers::formattedSpeedAndAltitude(
[MWMLocationManager lastLocation])];
}
- (void)setPlacePageForiPhoneWithOrientation:(UIInterfaceOrientation)orientation
@ -188,9 +185,10 @@ extern NSString * const kBookmarksChangedNotification;
withParameters:@{kStatValue : kStatDestination}];
[Alohalytics logEvent:kAlohalyticsTapEventKey withValue:@"ppRoute"];
LocationManager * lm = MapsAppDelegate.theApp.locationManager;
[self.delegate buildRouteFrom:lm.isLocationPendingOrNoPosition ? MWMRoutePoint::MWMRoutePointZero()
: MWMRoutePoint(lm.lastLocation.mercator)
CLLocation * lastLocation = [MWMLocationManager lastLocation];
BOOL const noLocation = location_helpers::isMyPositionPendingOrNoPosition() || !lastLocation;
[self.delegate buildRouteFrom:noLocation ? MWMRoutePoint::MWMRoutePointZero()
: MWMRoutePoint(lastLocation.mercator)
to:self.target];
}
@ -249,8 +247,6 @@ extern NSString * const kBookmarksChangedNotification;
- (void)book:(BOOL)isDescription
{
NSMutableDictionary * stat = [@{kStatProvider : kStatBooking} mutableCopy];
LocationManager * lm = MapsAppDelegate.theApp.locationManager;
CLLocation * loc = lm.lastLocationIsValid ? lm.lastLocation : nil;
MWMPlacePageEntity * en = self.entity;
auto const latLon = en.latlon;
stat[kStatHotel] = en.hotelId;
@ -258,7 +254,7 @@ extern NSString * const kBookmarksChangedNotification;
stat[kStatHotelLon] = @(latLon.lon);
[Statistics logEvent:isDescription ? kPlacePageHotelDetails : kPlacePageHotelBook
withParameters:stat
atLocation:loc];
atLocation:[MWMLocationManager lastLocation]];
UIViewController * vc = static_cast<UIViewController *>(MapsAppDelegate.theApp.mapViewController);
NSURL * url = isDescription ? [NSURL URLWithString:[self.entity getCellValue:MWMPlacePageCellTypeBookingMore]] : self.entity.bookingUrl;
@ -353,12 +349,6 @@ extern NSString * const kBookmarksChangedNotification;
[self.delegate dragPlacePage:frame];
}
- (void)onLocationUpdate:(location::GpsInfo const &)info
{
[self updateDistance];
[self updateMyPositionSpeedAndAltitude];
}
- (void)updateDistance
{
NSString * distance = [self distance];
@ -368,33 +358,17 @@ extern NSString * const kBookmarksChangedNotification;
- (NSString *)distance
{
CLLocation * location = [MapsAppDelegate theApp].locationManager.lastLocation;
// TODO(AlexZ): Do we REALLY need this check? Why this method is called if user mark/m_info is empty?
// TODO(AlexZ): Can location be checked before calling this method?
if (!location/* || !m_userMark*/)
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation)
return @"";
string distance;
CLLocationCoordinate2D const coord = location.coordinate;
CLLocationCoordinate2D const coord = lastLocation.coordinate;
ms::LatLon const target = self.entity.latlon;
MeasurementUtils::FormatDistance(ms::DistanceOnEarth(coord.latitude, coord.longitude,
target.lat, target.lon), distance);
return @(distance.c_str());
}
- (void)onCompassUpdate:(location::CompassInfo const &)info
{
CLLocation * location = [MapsAppDelegate theApp].locationManager.lastLocation;
// TODO(AlexZ): Do we REALLY need this check? Why compass update is here if user mark/m_info is empty?
// TODO(AlexZ): Can location be checked before calling this method?
if (!location/* || !m_userMark*/)
return;
CGFloat const angle = ang::AngleTo(location.mercator, self.entity.mercator) + info.m_bearing;
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_2 - angle);
[self.placePage setDirectionArrowTransform:transform];
[self.directionView setDirectionArrowTransform:transform];
}
- (void)showDirectionViewWithTitle:(NSString *)title type:(NSString *)type
{
MWMDirectionView * directionView = self.directionView;
@ -423,6 +397,25 @@ extern NSString * const kBookmarksChangedNotification;
((MWMiPadPlacePage *)self.placePage).height = height;
}
#pragma mark - MWMLocationObserver
- (void)onHeadingUpdate:(location::CompassInfo const &)info
{
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation)
return;
CGFloat const angle = ang::AngleTo(lastLocation.mercator, self.entity.mercator) + info.m_bearing;
CGAffineTransform transform = CGAffineTransformMakeRotation(M_PI_2 - angle);
[self.placePage setDirectionArrowTransform:transform];
[self.directionView setDirectionArrowTransform:transform];
}
- (void)onLocationUpdate:(location::GpsInfo const &)locationInfo
{
[self updateDistance];
[self updateMyPositionSpeedAndAltitude];
}
#pragma mark - Properties
- (MWMDirectionView *)directionView

View file

@ -1,5 +1,5 @@
#import "LocationManager.h"
#import "MapsAppDelegate.h"
#import "MWMLocationManager.h"
#import "MWMMapDownloaderExtendedDataSource.h"
#include "Framework.h"
@ -31,12 +31,12 @@ using namespace storage;
- (void)configNearMeSection
{
LocationManager * lm = MapsAppDelegate.theApp.locationManager;
if (!lm.lastLocationIsValid)
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation)
return;
auto & countryInfoGetter = GetFramework().CountryInfoGetter();
TCountriesVec closestCoutryIds;
countryInfoGetter.GetRegionsCountryId(lm.lastLocation.mercator, closestCoutryIds);
countryInfoGetter.GetRegionsCountryId(lastLocation.mercator, closestCoutryIds);
NSMutableArray<NSString *> * nearmeCountries = [@[] mutableCopy];
for (auto const & countryId : closestCoutryIds)
[nearmeCountries addObject:@(countryId.c_str())];

View file

@ -1,5 +1,3 @@
#import "LocationManager.h"
#import "LocationPredictor.h"
#import "MWMMapDownloaderTypes.h"
#import "MWMViewController.h"
#import <MyTargetSDKCorp/MTRGNativeAppwallAd.h>
@ -13,19 +11,12 @@ namespace search { struct AddressInfo; }
@class MWMMapViewControlsManager;
@class MWMAPIBar;
@interface MapViewController : MWMViewController <LocationObserver>
{
LocationPredictor * m_predictor;
}
@interface MapViewController : MWMViewController
// called when app is terminated by system
- (void)onTerminate;
- (void)onEnterForeground;
- (void)onEnterBackground;
- (void)onGetFocus:(BOOL)isOnFocus;
- (void)setMapStyle:(MapStyle)mapStyle;
- (void)updateStatusBarStyle;
- (void)showAPIBar;

View file

@ -14,6 +14,8 @@
#import "MWMFirstLaunchController.h"
#import "MWMFrameworkListener.h"
#import "MWMFrameworkObservers.h"
#import "MWMLocationHelpers.h"
#import "MWMLocationManager.h"
#import "MWMMapDownloadDialog.h"
#import "MWMMapDownloaderViewController.h"
#import "MWMMapViewControlsManager.h"
@ -115,7 +117,7 @@ BOOL gIsFirstMyPositionMode = YES;
@interface MapViewController ()<MTRGNativeAppwallAdDelegate, MWMFrameworkRouteBuilderObserver,
MWMFrameworkDrapeObserver, MWMFrameworkStorageObserver,
MWMPageControllerProtocol>
MWMPageControllerProtocol, MWMLocationObserver>
@property (nonatomic, readwrite) MWMMapViewControlsManager * controlsManager;
@property (nonatomic) MWMBottomMenuState menuRestoreState;
@ -133,45 +135,6 @@ BOOL gIsFirstMyPositionMode = YES;
@implementation MapViewController
#pragma mark - LocationManager Callbacks
- (void)onLocationError:(location::TLocationError)errorCode
{
GetFramework().OnLocationError(errorCode);
switch (errorCode)
{
case location::EDenied:
{
[self.alertController presentLocationAlert];
[[MapsAppDelegate theApp].locationManager stop:self];
break;
}
case location::ENotSupported:
{
[self.alertController presentLocationServiceNotSupportedAlert];
[[MapsAppDelegate theApp].locationManager stop:self];
break;
}
default:
break;
}
}
- (void)onLocationUpdate:(location::GpsInfo const &)info
{
if (info.m_source != location::EPredictor)
[m_predictor reset:info];
Framework & frm = GetFramework();
frm.OnLocationUpdate(info);
LOG_MEMORY_INFO();
[self updateRoutingInfo];
if (self.forceRoutingStateChange == ForceRoutingStateChangeRestoreRoute)
[self restoreRoute];
}
- (void)updateRoutingInfo
{
Framework & frm = GetFramework();
@ -188,9 +151,14 @@ BOOL gIsFirstMyPositionMode = YES;
[[MWMTextToSpeech tts] playTurnNotifications];
}
- (void)onCompassUpdate:(location::CompassInfo const &)info
#pragma mark - MWMLocationObserver
- (void)onLocationUpdate:(location::GpsInfo const &)info
{
GetFramework().OnCompassUpdate(info);
[self updateRoutingInfo];
if (self.forceRoutingStateChange == ForceRoutingStateChangeRestoreRoute)
[self restoreRoute];
}
#pragma mark - Restore route
@ -343,25 +311,6 @@ BOOL gIsFirstMyPositionMode = YES;
[(EAGLView *)self.view deallocateNative];
}
- (void)onEnterBackground
{
// Save state and notify about entering background.
GetFramework().EnterBackground();
}
- (void)setMapStyle:(MapStyle)mapStyle
{
GetFramework().SetMapStyle(mapStyle);
}
- (void)onEnterForeground
{
if (MapsAppDelegate.theApp.isDaemonMode)
return;
// Notify about entering foreground (should be called on the first launch too).
GetFramework().EnterForeground();
}
- (void)onGetFocus:(BOOL)isOnFocus
{
[(EAGLView *)self.view setPresentAvailable:isOnFocus];
@ -370,8 +319,6 @@ BOOL gIsFirstMyPositionMode = YES;
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (MapsAppDelegate.theApp.isDaemonMode)
return;
[[NSNotificationCenter defaultCenter] removeObserver:self name:UIDeviceOrientationDidChangeNotification object:nil];
self.controlsManager.menuState = self.menuRestoreState;
@ -382,6 +329,7 @@ BOOL gIsFirstMyPositionMode = YES;
GetFramework().InvalidateRendering();
[self showWelcomeScreenIfNeeded];
[self showViralAlertIfNeeded];
[MWMLocationManager addObserver:self];
}
- (void)viewDidAppear:(BOOL)animated
@ -393,8 +341,6 @@ BOOL gIsFirstMyPositionMode = YES;
- (void)viewDidLoad
{
[super viewDidLoad];
if (MapsAppDelegate.theApp.isDaemonMode)
return;
self.view.clipsToBounds = YES;
[MTRGManager setMyCom:YES];
[self processMyPositionStateModeEvent:location::PendingPosition];
@ -460,6 +406,7 @@ BOOL gIsFirstMyPositionMode = YES;
[super viewWillDisappear:animated];
self.menuRestoreState = self.controlsManager.menuState;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged:) name:UIDeviceOrientationDidChangeNotification object:nil];
[MWMLocationManager removeObserver:self];
}
- (void)presentViewController:(UIViewController *)viewControllerToPresent
@ -502,7 +449,7 @@ BOOL gIsFirstMyPositionMode = YES;
{
NSLog(@"MapViewController initWithCoder Started");
self = [super initWithCoder:coder];
if (self && !MapsAppDelegate.theApp.isDaemonMode)
if (self)
[self initialize];
NSLog(@"MapViewController initWithCoder Ended");
@ -524,7 +471,6 @@ BOOL gIsFirstMyPositionMode = YES;
[self processMyPositionStateModeEvent:mode];
});
m_predictor = [[LocationPredictor alloc] initWithObserver:self];
self.forceRoutingStateChange = ForceRoutingStateChangeNone;
self.userTouchesAction = UserTouchesActionNone;
self.menuRestoreState = MWMBottomMenuStateInactive;
@ -566,49 +512,35 @@ BOOL gIsFirstMyPositionMode = YES;
- (void)processMyPositionStateModeEvent:(location::EMyPositionMode)mode
{
[m_predictor setMode:mode];
LocationManager * lm = [MapsAppDelegate theApp].locationManager;
[lm processMyPositionStateModeEvent:mode];
[MWMLocationManager setMyPositionMode:mode];
[self.controlsManager processMyPositionStateModeEvent:mode];
self.disableStandbyOnLocationStateMode = NO;
switch (mode)
{
case location::PendingPosition:
self.disableStandbyOnLocationStateMode = NO;
[lm start:self];
break;
case location::NotFollowNoPosition:
if (gIsFirstMyPositionMode && ![Alohalytics isFirstSession])
case location::NotFollowNoPosition:
if (gIsFirstMyPositionMode && ![Alohalytics isFirstSession])
{
GetFramework().SwitchMyPositionNextMode();
}
else
{
BOOL const isMapVisible = (self.navigationController.visibleViewController == self);
if (isMapVisible && !location_helpers::isLocationProhibited())
{
GetFramework().SwitchMyPositionNextMode();
}
else
{
self.disableStandbyOnLocationStateMode = NO;
BOOL const isMapVisible = (self.navigationController.visibleViewController == self);
if (isMapVisible && lm.isStarted)
[self.alertController presentLocationNotFoundAlertWithOkBlock:^
{
[lm stop:self];
BOOL const isLocationProhibited =
lm.lastLocationError == location::TLocationError::EDenied ||
lm.lastLocationError == location::TLocationError::EGPSIsOff;
if (!isLocationProhibited)
{
[self.alertController presentLocationNotFoundAlertWithOkBlock:^
{
GetFramework().SwitchMyPositionNextMode();
}];
}
}
GetFramework().SwitchMyPositionNextMode();
}];
}
break;
case location::NotFollow:
self.disableStandbyOnLocationStateMode = NO;
break;
case location::Follow:
case location::FollowAndRotate:
self.disableStandbyOnLocationStateMode = YES;
break;
}
break;
case location::PendingPosition:
case location::NotFollow:
break;
case location::Follow:
case location::FollowAndRotate:
self.disableStandbyOnLocationStateMode = YES;
break;
}
gIsFirstMyPositionMode = NO;
}

View file

@ -29,8 +29,7 @@ typedef NS_ENUM(NSUInteger, MWMRoutingPlaneMode)
@property (nonatomic) MWMRoutingPlaneMode routingPlaneMode;
@property (nonatomic, readonly) MapViewController * mapViewController;
@property (nonatomic, readonly) LocationManager * locationManager;
@property (nonatomic, readonly) BOOL isDaemonMode;
@property (nonatomic, readonly) BOOL isDrapeEngineCreated;
+ (MapsAppDelegate *)theApp;

View file

@ -2,7 +2,6 @@
#import "Common.h"
#import "EAGLView.h"
#import "LocalNotificationManager.h"
#import "LocationManager.h"
#import "MapsAppDelegate.h"
#import "MapViewController.h"
#import "MWMAlertViewController.h"
@ -10,6 +9,7 @@
#import "MWMController.h"
#import "MWMFrameworkListener.h"
#import "MWMFrameworkObservers.h"
#import "MWMLocationManager.h"
#import "MWMStorage.h"
#import "MWMTextToSpeech.h"
#import "Preferences.h"
@ -140,7 +140,6 @@ using namespace osm_auth_ios;
@property (weak, nonatomic) NSTimer * mapStyleSwitchTimer;
@property (nonatomic, readwrite) LocationManager * locationManager;
@property (nonatomic, readwrite) BOOL isDaemonMode;
@end
@ -214,9 +213,14 @@ using namespace osm_auth_ios;
return YES;
}
- (BOOL)isDrapeEngineCreated
{
return ((EAGLView *)self.mapViewController.view).drapeEngineCreated;
}
- (void)handleURLs
{
if (!((EAGLView *)self.mapViewController.view).drapeEngineCreated)
if (!self.isDrapeEngineCreated)
{
dispatch_async(dispatch_get_main_queue(), ^{ [self handleURLs]; });
return;
@ -347,35 +351,40 @@ using namespace osm_auth_ios;
{
NSAssert([MapsAppDelegate isAutoNightMode], @"Invalid auto switcher's state");
auto & f = GetFramework();
MapsAppDelegate * app = MapsAppDelegate.theApp;
CLLocation * l = app.locationManager.lastLocation;
if (!l || !f.IsRoutingActive())
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation || !f.IsRoutingActive())
return;
dispatch_async(dispatch_get_main_queue(), [&f, l, self, app]
CLLocationCoordinate2D const coord = lastLocation.coordinate;
dispatch_async(dispatch_get_main_queue(), [coord]
{
auto const dayTime = GetDayTime(static_cast<time_t>(NSDate.date.timeIntervalSince1970), l.coordinate.latitude, l.coordinate.longitude);
id<MWMController> vc = static_cast<id<MWMController>>(app.mapViewController.navigationController.topViewController);
auto & f = GetFramework();
MapsAppDelegate * app = MapsAppDelegate.theApp;
auto const dayTime =
GetDayTime(static_cast<time_t>(NSDate.date.timeIntervalSince1970),
coord.latitude, coord.longitude);
id<MWMController> vc = static_cast<id<MWMController>>(
app.mapViewController.navigationController.topViewController);
auto style = f.GetMapStyle();
switch (dayTime)
{
case DayTimeType::Day:
case DayTimeType::PolarDay:
if (style != MapStyleClear && style != MapStyleLight)
{
f.SetMapStyle(MapStyleClear);
[UIColor setNightMode:NO];
[vc mwm_refreshUI];
}
break;
case DayTimeType::Night:
case DayTimeType::PolarNight:
if (style != MapStyleDark)
{
f.SetMapStyle(MapStyleDark);
[UIColor setNightMode:YES];
[vc mwm_refreshUI];
}
break;
case DayTimeType::Day:
case DayTimeType::PolarDay:
if (style != MapStyleClear && style != MapStyleLight)
{
f.SetMapStyle(MapStyleClear);
[UIColor setNightMode:NO];
[vc mwm_refreshUI];
}
break;
case DayTimeType::Night:
case DayTimeType::PolarNight:
if (style != MapStyleDark)
{
f.SetMapStyle(MapStyleDark);
[UIColor setNightMode:YES];
[vc mwm_refreshUI];
}
break;
}
});
}
@ -386,11 +395,6 @@ using namespace osm_auth_ios;
// Initialize all 3party engines.
BOOL returnValue = [self initStatistics:application didFinishLaunchingWithOptions:launchOptions];
if (launchOptions[UIApplicationLaunchOptionsLocationKey])
{
self.isDaemonMode = YES;
return returnValue;
}
// We send Alohalytics installation id to Fabric.
// To make sure id is created, ConfigCrashTrackers must be called after Statistics initialization.
@ -407,8 +411,7 @@ using namespace osm_auth_ios;
InitLocalizedStrings();
[self determineMapStyle];
[self.mapViewController onEnterForeground];
self.isDaemonMode = NO;
GetFramework().EnterForeground();
[self initPushNotificationsWithLaunchOptions:launchOptions];
[self commonInit];
@ -531,16 +534,13 @@ using namespace osm_auth_ios;
- (void)applicationWillTerminate:(UIApplication *)application
{
[self.locationManager beforeTerminate];
[self.mapViewController onTerminate];
}
- (void)applicationDidEnterBackground:(UIApplication *)application
{
LOG(LINFO, ("applicationDidEnterBackground"));
[self.locationManager stop:self.mapViewController];
[self.locationManager onBackground];
[self.mapViewController onEnterBackground];
GetFramework().EnterBackground();
if (m_activeDownloadsCounter)
{
m_backgroundTask = [application beginBackgroundTaskWithExpirationHandler:^{
@ -579,42 +579,25 @@ using namespace osm_auth_ios;
[self.mapViewController.appWallAd close];
[RouteState save];
GetFramework().SetRenderingEnabled(false);
[MWMLocationManager applicationWillResignActive];
}
- (void)applicationWillEnterForeground:(UIApplication *)application
{
LOG(LINFO, ("applicationWillEnterForeground"));
BOOL const needInit = self.isDaemonMode;
self.isDaemonMode = NO;
if (needInit)
{
[self.mapViewController initialize];
[(EAGLView *)self.mapViewController.view initialize];
[self.mapViewController.view setNeedsLayout];
[self.mapViewController.view layoutIfNeeded];
[self commonInit];
[self incrementSessionsCountAndCheckForAlert];
}
[self.mapViewController onEnterForeground];
GetFramework().EnterForeground();
[MWMTextToSpeech activateAudioSession];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
if (application.applicationState == UIApplicationStateBackground)
{
LOG(LINFO, ("applicationDidBecomeActive, applicationState = UIApplicationStateBackground"));
return;
}
LOG(LINFO, ("applicationDidBecomeActive"));
[self.mapViewController onGetFocus: YES];
[self handleURLs];
[self restoreRouteState];
[[Statistics instance] applicationDidBecomeActive];
GetFramework().SetRenderingEnabled(true);
if (![Alohalytics isFirstSession])
[self.locationManager start:self.mapViewController];
[MWMLocationManager applicationDidBecomeActive];
}
- (void)dealloc
@ -674,7 +657,7 @@ using namespace osm_auth_ios;
- (void)setMapStyle:(MapStyle)mapStyle
{
[self.mapViewController setMapStyle: mapStyle];
GetFramework().SetMapStyle(mapStyle);
}
+ (NSDictionary *)navigationBarTextAttributes
@ -829,33 +812,6 @@ using namespace osm_auth_ios;
return [(UINavigationController *)self.window.rootViewController viewControllers].firstObject;
}
- (LocationManager *)locationManager
{
if (!_locationManager)
_locationManager = [[LocationManager alloc] init];
return _locationManager;
}
@synthesize isDaemonMode = _isDaemonMode;
- (BOOL)isDaemonMode
{
if ([Alohalytics isFirstSession])
return NO;
return _isDaemonMode;
}
- (void)setIsDaemonMode:(BOOL)isDaemonMode
{
if ([Alohalytics isFirstSession] && _isDaemonMode == isDaemonMode)
return;
_isDaemonMode = isDaemonMode;
if (isDaemonMode)
[self.locationManager onDaemonMode];
else
[self.locationManager onForeground];
}
#pragma mark - Route state
- (void)restoreRouteState

View file

@ -1,7 +1,7 @@
#import "LocationManager.h"
#import "MapsAppDelegate.h"
#import "MWMAlertViewController.h"
#import "MWMCircularProgress.h"
#import "MWMLocationManager.h"
#import "MWMMapDownloaderViewController.h"
#import "MWMMigrationView.h"
#import "MWMMigrationViewController.h"
@ -64,10 +64,11 @@ using namespace storage;
[Statistics logEvent:kStatDownloaderMigrationStarted
withParameters:@{kStatType : limited ? kStatCurrentMap : kStatAllMaps}];
auto & f = GetFramework();
LocationManager * lm = [MapsAppDelegate theApp].locationManager;
ms::LatLon position{};
if (![lm getLat:position.lat Lon:position.lon])
position = MercatorBounds::ToLatLon(f.GetViewportCenter());
ms::LatLon position = MercatorBounds::ToLatLon(f.GetViewportCenter());
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (lastLocation)
position = ms::LatLon(lastLocation.coordinate.latitude, lastLocation.coordinate.longitude);
auto migrate = ^
{

View file

@ -1,11 +1,11 @@
#import "Common.h"
#import "LocationManager.h"
#import "MapsAppDelegate.h"
#import "MapViewController.h"
#import "MWMAlertViewController.h"
#import "MWMCircularProgress.h"
#import "MWMFrameworkListener.h"
#import "MWMFrameworkObservers.h"
#import "MWMLocationManager.h"
#import "MWMMapDownloadDialog.h"
#import "MWMStorage.h"
#import "Statistics.h"
@ -27,13 +27,13 @@ BOOL canAutoDownload(TCountryId const & countryId)
(void)settings::Get(kAutoDownloadEnabledKey, autoDownloadEnabled);
if (!autoDownloadEnabled)
return NO;
LocationManager * locationManager = MapsAppDelegate.theApp.locationManager;
if (![locationManager lastLocationIsValid])
return NO;
if (GetPlatform().ConnectionStatus() != Platform::EConnectionType::CONNECTION_WIFI)
return NO;
CLLocation * lastLocation = [MWMLocationManager lastLocation];
if (!lastLocation)
return NO;
auto const & countryInfoGetter = GetFramework().CountryInfoGetter();
if (countryId != countryInfoGetter.GetRegionCountryId(locationManager.lastLocation.mercator))
if (countryId != countryInfoGetter.GetRegionCountryId(lastLocation.mercator))
return NO;
return !platform::migrate::NeedMigrate();
}

View file

@ -144,8 +144,8 @@
</dict>
</dict>
</dict>
<key>NSLocationAlwaysUsageDescription</key>
<string></string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>#description#</string>
<key>UIApplicationShortcutItems</key>
<array>
<dict>

View file

@ -97,6 +97,8 @@
343FAC4B1CBFBDFC00A45D3B /* MWMNoMapsView.mm in Sources */ = {isa = PBXBuildFile; fileRef = 343FAC491CBFBDFC00A45D3B /* MWMNoMapsView.mm */; };
34479C7C1C60C6130065D261 /* MWMFrameworkListener.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34479C781C60C6130065D261 /* MWMFrameworkListener.mm */; };
34479C7D1C60C6130065D261 /* MWMFrameworkListener.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34479C781C60C6130065D261 /* MWMFrameworkListener.mm */; };
344D77B41D1BD7C800DBED70 /* MWMLocationManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 344D77B31D1BD7C800DBED70 /* MWMLocationManager.mm */; };
344D77B51D1BD7C800DBED70 /* MWMLocationManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = 344D77B31D1BD7C800DBED70 /* MWMLocationManager.mm */; };
34570A391B13219600E6D4FD /* AudioToolbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97C98653186C5F0500AF7E9E /* AudioToolbox.framework */; settings = {ATTRIBUTES = (Required, ); }; };
34570A3B1B13222600E6D4FD /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 34570A3A1B13222600E6D4FD /* libz.dylib */; settings = {ATTRIBUTES = (Required, ); }; };
34570A3D1B13223000E6D4FD /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 34570A3C1B13223000E6D4FD /* libsqlite3.dylib */; settings = {ATTRIBUTES = (Required, ); }; };
@ -288,6 +290,7 @@
34D349FC1CD0A3EE00895216 /* MWMWhatsNewEditorController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34D349FA1CD0A3EE00895216 /* MWMWhatsNewEditorController.mm */; };
34D37E171CD2373C001DEFC3 /* MWMMapDownloaderCellHeader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34D37E161CD2373C001DEFC3 /* MWMMapDownloaderCellHeader.mm */; };
34D37E181CD2373C001DEFC3 /* MWMMapDownloaderCellHeader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34D37E161CD2373C001DEFC3 /* MWMMapDownloaderCellHeader.mm */; };
34D866DE1D213E8B00B593F8 /* MWMLocationPredictor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34FED54C1D1D45B900183B1B /* MWMLocationPredictor.mm */; };
34DCDE3A1C75CD8100652CAC /* Framework.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 34479C751C60C6130065D261 /* Framework.cpp */; };
34DCDE3B1C75CE1F00652CAC /* MWMOpeningHoursDeleteScheduleTableViewCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 347FD85A1C60B2CE002FB65E /* MWMOpeningHoursDeleteScheduleTableViewCell.mm */; };
34DDD5341BFDB0B600407F2F /* MWMMapDownloaderViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 342AF0DF1BE24E9A0016F3AE /* MWMMapDownloaderViewController.mm */; };
@ -317,6 +320,9 @@
34F9FB921C43AF2400F71201 /* MWMStreetEditorEditTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34F9FB8F1C43AF2400F71201 /* MWMStreetEditorEditTableViewCell.xib */; };
34F9FB931C43AF2400F71201 /* MWMStreetEditorEditTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 34F9FB8F1C43AF2400F71201 /* MWMStreetEditorEditTableViewCell.xib */; };
34FE4C451BCC013500066718 /* MWMMapWidgets.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34FE4C441BCC013500066718 /* MWMMapWidgets.mm */; };
34FED54D1D1D45B900183B1B /* MWMLocationPredictor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34FED54C1D1D45B900183B1B /* MWMLocationPredictor.mm */; };
34FED5501D21121000183B1B /* CLLocation+Mercator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34FED54F1D21121000183B1B /* CLLocation+Mercator.mm */; };
34FED5511D21121000183B1B /* CLLocation+Mercator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34FED54F1D21121000183B1B /* CLLocation+Mercator.mm */; };
4519503A1B7A3E070085DA05 /* patterns.txt in Resources */ = {isa = PBXBuildFile; fileRef = 451950391B7A3E070085DA05 /* patterns.txt */; };
452FCA3B1B6A3DF7007019AB /* colors.txt in Resources */ = {isa = PBXBuildFile; fileRef = 452FCA3A1B6A3DF7007019AB /* colors.txt */; };
46F26C7310F61FD600ECCA39 /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 46F26C7210F61FD600ECCA39 /* OpenGLES.framework */; };
@ -448,7 +454,6 @@
6741A9B81BF340DE002C974C /* MapViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = EED10A4411F78D120095FAD4 /* MapViewController.mm */; };
6741A9B91BF340DE002C974C /* MWMRateAlert.mm in Sources */ = {isa = PBXBuildFile; fileRef = F61579331AC2CE9A0032D8E9 /* MWMRateAlert.mm */; };
6741A9BA1BF340DE002C974C /* MWMRoutePointCell.m in Sources */ = {isa = PBXBuildFile; fileRef = F6BB6CC51BB18C0900DF1DF2 /* MWMRoutePointCell.m */; };
6741A9BB1BF340DE002C974C /* LocationPredictor.mm in Sources */ = {isa = PBXBuildFile; fileRef = A3CC2CD21A1C723900B832E1 /* LocationPredictor.mm */; };
6741A9BE1BF340DE002C974C /* UILabel+RuntimeAttributes.mm in Sources */ = {isa = PBXBuildFile; fileRef = F62404FA1AAF3DB200B58DB6 /* UILabel+RuntimeAttributes.mm */; };
6741A9BF1BF340DE002C974C /* MWMSearchTabbedCollectionViewCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34CC4C0C1B82069C00E44C1F /* MWMSearchTabbedCollectionViewCell.mm */; };
6741A9C01BF340DE002C974C /* MWMTextView.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6588E2B1B15C26700EE1E58 /* MWMTextView.mm */; };
@ -486,7 +491,6 @@
6741A9E91BF340DE002C974C /* Preferences.mm in Sources */ = {isa = PBXBuildFile; fileRef = FA29FDA9141E77F8004ADF66 /* Preferences.mm */; };
6741A9EB1BF340DE002C974C /* ContextViews.mm in Sources */ = {isa = PBXBuildFile; fileRef = F6D409F91B319BD70041730F /* ContextViews.mm */; };
6741A9EC1BF340DE002C974C /* MWMCircularProgress.mm in Sources */ = {isa = PBXBuildFile; fileRef = 349A35761B53D4C9009677EE /* MWMCircularProgress.mm */; };
6741A9ED1BF340DE002C974C /* LocationManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = FAA5C2A1144F135F005337F6 /* LocationManager.mm */; };
6741A9EE1BF340DE002C974C /* MWMSearchCell.mm in Sources */ = {isa = PBXBuildFile; fileRef = 34B82AD91B847FFC00180497 /* MWMSearchCell.mm */; };
6741A9EF1BF340DE002C974C /* MWMiPadPlacePage.mm in Sources */ = {isa = PBXBuildFile; fileRef = F66A8FAB1B09F137001B9C97 /* MWMiPadPlacePage.mm */; };
6741A9F01BF340DE002C974C /* SettingsViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 978F9239183B660F000D6C7C /* SettingsViewController.mm */; };
@ -653,7 +657,6 @@
A32B6D4C1A14980500E54A65 /* iosOGLContext.mm in Sources */ = {isa = PBXBuildFile; fileRef = A32B6D491A14980500E54A65 /* iosOGLContext.mm */; };
A32B6D4D1A14980500E54A65 /* iosOGLContextFactory.mm in Sources */ = {isa = PBXBuildFile; fileRef = A32B6D4B1A14980500E54A65 /* iosOGLContextFactory.mm */; };
A367C93B1B17334800E2B6E7 /* resources-default in Resources */ = {isa = PBXBuildFile; fileRef = A367C93A1B17334800E2B6E7 /* resources-default */; };
A3CC2CD41A1C723900B832E1 /* LocationPredictor.mm in Sources */ = {isa = PBXBuildFile; fileRef = A3CC2CD21A1C723900B832E1 /* LocationPredictor.mm */; };
B00510FB1A11015900A61AA4 /* CFNetwork.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 972CDCC51887F1B7006641CA /* CFNetwork.framework */; };
B00511021A1101E000A61AA4 /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 97C98655186C734000AF7E9E /* AVFoundation.framework */; };
B00511041A1101F600A61AA4 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B00511031A1101F600A61AA4 /* CoreData.framework */; };
@ -837,7 +840,6 @@
FA85F633145DDDC20090E1A0 /* packed_polygons.bin in Resources */ = {isa = PBXBuildFile; fileRef = FA85F632145DDDC20090E1A0 /* packed_polygons.bin */; };
FA87151B12B1518F00592DAF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA87151A12B1518F00592DAF /* SystemConfiguration.framework */; };
FA99CB73147089B100689A9A /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = FA99CB71147089B100689A9A /* Localizable.strings */; };
FAA5C2A2144F135F005337F6 /* LocationManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = FAA5C2A1144F135F005337F6 /* LocationManager.mm */; };
FAA614B8155F16950031C345 /* AddSetVC.mm in Sources */ = {isa = PBXBuildFile; fileRef = FAA614B7155F16950031C345 /* AddSetVC.mm */; };
FAAEA7D1161BD26600CCD661 /* synonyms.txt in Resources */ = {isa = PBXBuildFile; fileRef = FAAEA7D0161BD26600CCD661 /* synonyms.txt */; };
FAAEA7D5161D8D3100CCD661 /* BookmarksRootVC.mm in Sources */ = {isa = PBXBuildFile; fileRef = FAAEA7D3161D8D3100CCD661 /* BookmarksRootVC.mm */; };
@ -991,6 +993,8 @@
34479C781C60C6130065D261 /* MWMFrameworkListener.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMFrameworkListener.mm; sourceTree = "<group>"; };
34479C791C60C6130065D261 /* MWMFrameworkObservers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMFrameworkObservers.h; sourceTree = "<group>"; };
344BDB021B9069810076DB31 /* MWMSearchTabbedViewProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMSearchTabbedViewProtocol.h; sourceTree = "<group>"; };
344D77B21D1BD7C800DBED70 /* MWMLocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMLocationManager.h; sourceTree = "<group>"; };
344D77B31D1BD7C800DBED70 /* MWMLocationManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMLocationManager.mm; sourceTree = "<group>"; };
34570A3A1B13222600E6D4FD /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
34570A3C1B13223000E6D4FD /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; };
34570A3E1B13225500E6D4FD /* Accounts.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accounts.framework; path = System/Library/Frameworks/Accounts.framework; sourceTree = SDKROOT; };
@ -1221,6 +1225,11 @@
34F9FB8F1C43AF2400F71201 /* MWMStreetEditorEditTableViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = MWMStreetEditorEditTableViewCell.xib; sourceTree = "<group>"; };
34FE4C431BCC013500066718 /* MWMMapWidgets.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMMapWidgets.h; sourceTree = "<group>"; };
34FE4C441BCC013500066718 /* MWMMapWidgets.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMMapWidgets.mm; sourceTree = "<group>"; };
34FED54B1D1D45B900183B1B /* MWMLocationPredictor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MWMLocationPredictor.h; sourceTree = "<group>"; };
34FED54C1D1D45B900183B1B /* MWMLocationPredictor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MWMLocationPredictor.mm; sourceTree = "<group>"; };
34FED54E1D21121000183B1B /* CLLocation+Mercator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CLLocation+Mercator.h"; sourceTree = "<group>"; };
34FED54F1D21121000183B1B /* CLLocation+Mercator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = "CLLocation+Mercator.mm"; sourceTree = "<group>"; };
34FED5521D2123CE00183B1B /* MWMLocationHelpers.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MWMLocationHelpers.h; sourceTree = "<group>"; };
3D443C9C19E421EE0025C2FC /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = "<group>"; };
451950391B7A3E070085DA05 /* patterns.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = patterns.txt; path = ../../data/patterns.txt; sourceTree = "<group>"; };
452FCA3A1B6A3DF7007019AB /* colors.txt */ = {isa = PBXFileReference; lastKnownFileType = text; name = colors.txt; path = ../../data/colors.txt; sourceTree = "<group>"; };
@ -1347,8 +1356,6 @@
A32B6D4A1A14980500E54A65 /* iosOGLContextFactory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = iosOGLContextFactory.h; path = Platform/opengl/iosOGLContextFactory.h; sourceTree = "<group>"; };
A32B6D4B1A14980500E54A65 /* iosOGLContextFactory.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = iosOGLContextFactory.mm; path = Platform/opengl/iosOGLContextFactory.mm; sourceTree = "<group>"; };
A367C93A1B17334800E2B6E7 /* resources-default */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "resources-default"; path = "../../data/resources-default"; sourceTree = "<group>"; };
A3CC2CD21A1C723900B832E1 /* LocationPredictor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LocationPredictor.mm; sourceTree = "<group>"; };
A3CC2CD31A1C723900B832E1 /* LocationPredictor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LocationPredictor.h; sourceTree = "<group>"; };
B00511031A1101F600A61AA4 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
B00511051A1101FC00A61AA4 /* CoreMedia.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreMedia.framework; path = System/Library/Frameworks/CoreMedia.framework; sourceTree = SDKROOT; };
B08AA8D81A26299A00810B1C /* TimeUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TimeUtils.h; path = Categories/TimeUtils.h; sourceTree = "<group>"; };
@ -1587,8 +1594,6 @@
FA971562150920C600916690 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = "<group>"; };
FA99CB72147089B100689A9A /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
FAA4B13E13EC1C8C00BCAB63 /* DiskFreeSpace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiskFreeSpace.h; sourceTree = "<group>"; };
FAA5C2A0144F135F005337F6 /* LocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LocationManager.h; path = Platform/LocationManager.h; sourceTree = "<group>"; };
FAA5C2A1144F135F005337F6 /* LocationManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; lineEnding = 0; name = LocationManager.mm; path = Platform/LocationManager.mm; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
FAA614B6155F16950031C345 /* AddSetVC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AddSetVC.h; path = Bookmarks/AddSetVC.h; sourceTree = SOURCE_ROOT; };
FAA614B7155F16950031C345 /* AddSetVC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AddSetVC.mm; path = Bookmarks/AddSetVC.mm; sourceTree = SOURCE_ROOT; };
FAAEA7D0161BD26600CCD661 /* synonyms.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = synonyms.txt; path = ../../data/synonyms.txt; sourceTree = "<group>"; };
@ -1738,6 +1743,7 @@
080E96DDFE201D6D7F000001 /* Classes */ = {
isa = PBXGroup;
children = (
344D77B11D1BD79700DBED70 /* Location */,
34479C741C60C6130065D261 /* Framework */,
34CE8A641C7740CF00F4351A /* Storage */,
34ABA61D1C2D514A00FE1BEC /* Input Validators */,
@ -1766,8 +1772,6 @@
348D1DF91C525B8300860465 /* MWMTypes.h */,
F6DF5F321CD1136800A87154 /* LocaleTranslator.h */,
F6381BF41CD12045004CA943 /* LocaleTranslator.mm */,
A3CC2CD21A1C723900B832E1 /* LocationPredictor.mm */,
A3CC2CD31A1C723900B832E1 /* LocationPredictor.h */,
458287C21AD3BE2000BA8940 /* DownloadIndicatorProtocol.h */,
F6F9BD221AD3C3A600308C33 /* Macros.h */,
);
@ -2033,6 +2037,18 @@
path = Framework;
sourceTree = "<group>";
};
344D77B11D1BD79700DBED70 /* Location */ = {
isa = PBXGroup;
children = (
34FED54B1D1D45B900183B1B /* MWMLocationPredictor.h */,
34FED54C1D1D45B900183B1B /* MWMLocationPredictor.mm */,
344D77B21D1BD7C800DBED70 /* MWMLocationManager.h */,
344D77B31D1BD7C800DBED70 /* MWMLocationManager.mm */,
34FED5521D2123CE00183B1B /* MWMLocationHelpers.h */,
);
path = Location;
sourceTree = "<group>";
};
345C34661CE9D32500BB2224 /* SideButtons */ = {
isa = PBXGroup;
children = (
@ -2567,6 +2583,8 @@
34ABA61A1C2D4DCC00FE1BEC /* UITextField+RuntimeAttributes.mm */,
F64DA8001C524BDE00330E9E /* UISwitch+RuntimeAttributes.h */,
F64DA8011C524BDE00330E9E /* UISwitch+RuntimeAttributes.m */,
34FED54E1D21121000183B1B /* CLLocation+Mercator.h */,
34FED54F1D21121000183B1B /* CLLocation+Mercator.mm */,
);
name = Categories;
sourceTree = "<group>";
@ -3189,8 +3207,6 @@
A32B6D491A14980500E54A65 /* iosOGLContext.mm */,
A32B6D4A1A14980500E54A65 /* iosOGLContextFactory.h */,
A32B6D4B1A14980500E54A65 /* iosOGLContextFactory.mm */,
FAA5C2A0144F135F005337F6 /* LocationManager.h */,
FAA5C2A1144F135F005337F6 /* LocationManager.mm */,
);
name = Platform;
sourceTree = "<group>";
@ -3660,11 +3676,13 @@
F6BC1E521ACBF98600EF0360 /* MWMFacebookAlert.mm in Sources */,
F64F199D1AB81A00006EAF7E /* MWMDefaultAlert.mm in Sources */,
F6E9BF321CE3658D0050E534 /* MWMBookmarkTitleCell.m in Sources */,
344D77B41D1BD7C800DBED70 /* MWMLocationManager.mm in Sources */,
3438CDF81B8616760051AA78 /* MWMSearchShowOnMapCell.mm in Sources */,
34F45E8E1B96E88100AC93F8 /* MWMSearchTabButtonsView.mm in Sources */,
3490D2DE1CE9DD2500D0B838 /* MWMSideButtons.mm in Sources */,
342FDDE51C6C9071000038A0 /* MWMMapDownloaderDataSource.mm in Sources */,
347FD86B1C60B2CE002FB65E /* MWMOpeningHoursAddScheduleTableViewCell.mm in Sources */,
34FED5501D21121000183B1B /* CLLocation+Mercator.mm in Sources */,
34CD81BB1C91C24D007D2A60 /* MWMWelcomeController.mm in Sources */,
F6C934401AE64E4200DDC624 /* MWMSpringAnimation.mm in Sources */,
34B82ACA1B8465C100180497 /* MWMSearchCategoryCell.mm in Sources */,
@ -3683,7 +3701,6 @@
34CCFDD11C21945500F28959 /* MWMPlacePageOpeningHoursDayView.mm in Sources */,
F61579341AC2CE9A0032D8E9 /* MWMRateAlert.mm in Sources */,
F6BB6CC61BB18C0900DF1DF2 /* MWMRoutePointCell.m in Sources */,
A3CC2CD41A1C723900B832E1 /* LocationPredictor.mm in Sources */,
347D7C691C2C0703006B2D0A /* UITextView+RuntimeAttributes.mm in Sources */,
34181EB91C0ED1C30081B586 /* MWMOpeningHoursSection.mm in Sources */,
F6D4A72F1CC1030E00BD4E5B /* MWMEditorNotesFooter.mm in Sources */,
@ -3758,7 +3775,6 @@
347FD8851C60B2CE002FB65E /* MWMOpeningHoursTimeSelectorTableViewCell.mm in Sources */,
34EB84581C073DF70004689F /* MWMOpeningHoursEditorViewController.mm in Sources */,
349A357A1B53D4C9009677EE /* MWMCircularProgress.mm in Sources */,
FAA5C2A2144F135F005337F6 /* LocationManager.mm in Sources */,
34B82ADA1B847FFC00180497 /* MWMSearchCell.mm in Sources */,
F6FEA82D1C58E89B007223CC /* MWMButton.mm in Sources */,
F66A8FAC1B09F137001B9C97 /* MWMiPadPlacePage.mm in Sources */,
@ -3823,6 +3839,7 @@
34B82AE21B84AC5E00180497 /* MWMSearchCategoriesManager.mm in Sources */,
34CE8A671C7740E100F4351A /* MWMStorage.mm in Sources */,
F6F533A31B3C248900C1940B /* UIColor+MapsMeColor.mm in Sources */,
34FED54D1D1D45B900183B1B /* MWMLocationPredictor.mm in Sources */,
346EDADB1B9F0E35004F8DB5 /* MWMMultilineLabel.mm in Sources */,
34C9BD041C6DB693000DC38D /* MWMViewController.mm in Sources */,
CB252D6F16FF82C9001E41E9 /* Statistics.mm in Sources */,
@ -3877,6 +3894,7 @@
buildActionMask = 2147483647;
files = (
6741A9A21BF340DE002C974C /* CommunityVC.mm in Sources */,
34FED5511D21121000183B1B /* CLLocation+Mercator.mm in Sources */,
6741A9A31BF340DE002C974C /* main.mm in Sources */,
6741A9A41BF340DE002C974C /* MWMSearchTabbedViewController.mm in Sources */,
6741A9A51BF340DE002C974C /* MWMShareLocationActivityItem.mm in Sources */,
@ -3886,6 +3904,7 @@
6741A9A81BF340DE002C974C /* MWMFacebookAlert.mm in Sources */,
6741A9A91BF340DE002C974C /* MWMDefaultAlert.mm in Sources */,
F6E9BF331CE3658D0050E534 /* MWMBookmarkTitleCell.m in Sources */,
344D77B51D1BD7C800DBED70 /* MWMLocationManager.mm in Sources */,
6741A9AA1BF340DE002C974C /* MWMSearchShowOnMapCell.mm in Sources */,
6741A9AC1BF340DE002C974C /* MWMSearchTabButtonsView.mm in Sources */,
3490D2DF1CE9DD2500D0B838 /* MWMSideButtons.mm in Sources */,
@ -3907,7 +3926,6 @@
34CCFDD21C21945500F28959 /* MWMPlacePageOpeningHoursDayView.mm in Sources */,
6741A9B91BF340DE002C974C /* MWMRateAlert.mm in Sources */,
6741A9BA1BF340DE002C974C /* MWMRoutePointCell.m in Sources */,
6741A9BB1BF340DE002C974C /* LocationPredictor.mm in Sources */,
347D7C6A1C2C0703006B2D0A /* UITextView+RuntimeAttributes.mm in Sources */,
6741A9BE1BF340DE002C974C /* UILabel+RuntimeAttributes.mm in Sources */,
6741A9BF1BF340DE002C974C /* MWMSearchTabbedCollectionViewCell.mm in Sources */,
@ -3948,6 +3966,7 @@
6741A9D41BF340DE002C974C /* MWMAlertViewController.mm in Sources */,
F6FE3C391CC50FFD00A73196 /* MWMPlaceDoesntExistAlert.mm in Sources */,
34C9BD0A1C6DBCDA000DC38D /* MWMNavigationController.mm in Sources */,
34D866DE1D213E8B00B593F8 /* MWMLocationPredictor.mm in Sources */,
3406FA161C6E0C3300E9FAD2 /* MWMMapDownloadDialog.mm in Sources */,
6741A9D51BF340DE002C974C /* WebViewController.mm in Sources */,
34C9BD031C6DB693000DC38D /* MWMTableViewController.mm in Sources */,
@ -3981,7 +4000,6 @@
6741A9EB1BF340DE002C974C /* ContextViews.mm in Sources */,
6741A9EC1BF340DE002C974C /* MWMCircularProgress.mm in Sources */,
342CC5F21C2D7730005F3FE5 /* MWMAuthorizationLoginViewController.mm in Sources */,
6741A9ED1BF340DE002C974C /* LocationManager.mm in Sources */,
6741A9EE1BF340DE002C974C /* MWMSearchCell.mm in Sources */,
6741A9EF1BF340DE002C974C /* MWMiPadPlacePage.mm in Sources */,
6741A9F01BF340DE002C974C /* SettingsViewController.mm in Sources */,

View file

@ -1,70 +0,0 @@
#import <Foundation/Foundation.h>
#import <CoreLocation/CoreLocation.h>
#import <UIKit/UIApplication.h>
#include "geometry/mercator.hpp"
#include "platform/location.hpp"
#include "std/utility.hpp"
@protocol LocationObserver
@required
- (void)onLocationUpdate:(location::GpsInfo const &)info;
@optional
- (void)onLocationError:(location::TLocationError)errorCode;
- (void)onCompassUpdate:(location::CompassInfo const &)info;
@end
@interface LocationManager : NSObject <CLLocationManagerDelegate>
@property (nonatomic, readonly) BOOL isStarted;
@property (nonatomic, readonly) location::TLocationError lastLocationError;
- (void)start:(id <LocationObserver>)observer;
- (void)stop:(id <LocationObserver>)observer;
- (CLLocation *)lastLocation;
- (CLHeading *)lastHeading;
// Fixes compass angle orientation when rotating screen to landscape
- (void)orientationChanged;
- (bool)getLat:(double &)lat Lon:(double &)lon;
- (bool)getNorthRad:(double &)rad;
+ (NSString *)formattedDistance:(double)meters;
// Returns nil if neither speed nor altitude are available
- (NSString *)formattedSpeedAndAltitude:(BOOL &)hasSpeed;
- (bool)lastLocationIsValid;
- (bool)isLocationPendingOrNoPosition;
- (BOOL)enabledOnMap;
- (void)onDaemonMode;
- (void)onForeground;
- (void)onBackground;
- (void)beforeTerminate;
- (void)reset;
- (void)processMyPositionStateModeEvent:(location::EMyPositionMode)mode;
@end
@interface CLLocation (Mercator)
- (m2::PointD)mercator;
@end
static inline ms::LatLon ToLatLon(m2::PointD const & p)
{
return MercatorBounds::ToLatLon(p);
}
static inline m2::PointD ToMercator(CLLocationCoordinate2D const & l)
{
return MercatorBounds::FromLatLon(l.latitude, l.longitude);
}
static inline m2::PointD ToMercator(ms::LatLon const & l)
{
return MercatorBounds::FromLatLon(l);
}

View file

@ -1,549 +0,0 @@
#import "Common.h"
#import "LocationManager.h"
#import "MapsAppDelegate.h"
#import "MapViewController.h"
#import "MWMMapViewControlsManager.h"
#import "Statistics.h"
#import "3party/Alohalytics/src/alohalytics_objc.h"
#include "Framework.h"
#include "map/gps_tracker.hpp"
#include "routing/router.hpp"
#include "platform/file_logging.hpp"
#include "platform/measurement_utils.hpp"
#include "platform/settings.hpp"
#include "base/math.hpp"
static CLAuthorizationStatus const kRequestAuthStatus = kCLAuthorizationStatusAuthorizedAlways;
static NSString * const kAlohalyticsLocationRequestAlwaysFailed = @"$locationAlwaysRequestErrorDenied";
namespace
{
enum class GeoMode
{
Pending,
InPosition,
NotInPosition,
FollowAndRotate,
VehicleRouting,
PedestrianRouting,
BicycleRouting
};
struct DesiredAccuracy
{
CLLocationAccuracy charging;
CLLocationAccuracy battery;
};
struct GeoModeSettings
{
CLLocationDistance distanceFilter;
DesiredAccuracy accuracy;
};
map<GeoMode, GeoModeSettings> const kGeoSettings{
{GeoMode::Pending,
{.distanceFilter = kCLDistanceFilterNone,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBestForNavigation}}},
{GeoMode::InPosition,
{.distanceFilter = 2,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBest}}},
{GeoMode::NotInPosition,
{.distanceFilter = 5,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBest}}},
{GeoMode::FollowAndRotate,
{.distanceFilter = 2,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBest}}},
{GeoMode::VehicleRouting,
{.distanceFilter = kCLDistanceFilterNone,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBest}}},
{GeoMode::PedestrianRouting,
{.distanceFilter = 2,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBest}}},
{GeoMode::BicycleRouting,
{.distanceFilter = 2,
.accuracy = {.charging = kCLLocationAccuracyBestForNavigation,
.battery = kCLLocationAccuracyBest}}}};
} // namespace
@interface LocationManager ()
@property (nonatomic) BOOL deferringUpdates;
@property (nonatomic) CLLocationManager * locationManager;
@property (nonatomic, readwrite) BOOL isStarted;
@property (nonatomic, readwrite) location::TLocationError lastLocationError;
@property (nonatomic) NSMutableSet * observers;
@property (nonatomic) NSDate * lastLocationTime;
@property (nonatomic) GeoMode geoMode;
@end
@implementation LocationManager
- (void)batteryStateChangedNotification:(NSNotification *)notification
{
[self refreshGeoModeSettings];
}
- (void)refreshGeoModeSettings
{
UIDeviceBatteryState const state = [UIDevice currentDevice].batteryState;
BOOL const isCharging = (state == UIDeviceBatteryStateCharging || state == UIDeviceBatteryStateFull);
GeoModeSettings const settings = kGeoSettings.at(self.geoMode);
self.locationManager.desiredAccuracy = isCharging ? settings.accuracy.charging : settings.accuracy.battery;
self.locationManager.distanceFilter = settings.distanceFilter;
}
- (void)dealloc
{
[self reset];
}
- (void)onDaemonMode
{
[self.locationManager stopMonitoringSignificantLocationChanges];
LOG(LINFO, ("startUpdatingLocation"));
[self.locationManager startUpdatingLocation];
}
- (void)onBackground
{
if (!GpsTracker::Instance().IsEnabled())
{
LOG(LINFO, ("stopUpdatingLocation"));
[self.locationManager stopUpdatingLocation];
}
}
- (void)beforeTerminate
{
if (!GpsTracker::Instance().IsEnabled())
return;
[self.locationManager startMonitoringSignificantLocationChanges];
}
- (void)onForeground
{
[self onDaemonMode];
[self.locationManager disallowDeferredLocationUpdates];
self.deferringUpdates = NO;
[self orientationChanged];
}
- (void)start:(id <LocationObserver>)observer
{
if (!self.isStarted)
{
//YES if location services are enabled; NO if they are not.
if ([CLLocationManager locationServicesEnabled])
{
CLAuthorizationStatus const authStatus = [CLLocationManager authorizationStatus];
switch (authStatus)
{
case kCLAuthorizationStatusAuthorizedWhenInUse:
case kCLAuthorizationStatusAuthorizedAlways:
case kCLAuthorizationStatusNotDetermined:
if (kRequestAuthStatus == kCLAuthorizationStatusAuthorizedAlways && [self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
[self.locationManager requestAlwaysAuthorization];
LOG(LINFO, ("startUpdatingLocation"));
[self.locationManager startUpdatingLocation];
if ([CLLocationManager headingAvailable])
[self.locationManager startUpdatingHeading];
self.isStarted = YES;
[self.observers addObject:observer];
break;
case kCLAuthorizationStatusRestricted:
case kCLAuthorizationStatusDenied:
[self observer:observer onLocationError:location::EDenied];
break;
}
}
else
[self observer:observer onLocationError:location::EGPSIsOff];
}
else
{
BOOL updateLocation = ![self.observers containsObject:observer];
[self.observers addObject:observer];
if ([self lastLocationIsValid] && updateLocation)
{
// pass last location known location when new observer is attached
// (default CLLocationManagerDelegate behaviour)
[observer onLocationUpdate:gpsInfoFromLocation(self.lastLocation)];
}
}
}
- (void)stop:(id <LocationObserver>)observer
{
[self.observers removeObject:observer];
if (self.isStarted && self.observers.count == 0)
{
// stop only if no more observers are subsribed
self.isStarted = NO;
if ([CLLocationManager headingAvailable])
[self.locationManager stopUpdatingHeading];
LOG(LINFO, ("stopUpdatingLocation"));
[self.locationManager stopUpdatingLocation];
}
}
- (CLLocation *)lastLocation
{
if (!self.isStarted || self.locationManager.location.horizontalAccuracy < 0.)
return nil;
return self.locationManager.location;
}
- (CLHeading *)lastHeading
{
if (!self.isStarted || self.locationManager.heading.headingAccuracy < 0.)
return nil;
return self.locationManager.heading;
}
- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)heading
{
[self notifyCompassUpdate:compasInfoFromHeading(heading)];
}
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray<CLLocation *> *)locations
{
[self processLocation:locations.lastObject];
if (!self.deferringUpdates)
return;
[self.locationManager allowDeferredLocationUpdatesUntilTraveled:300 timeout:15];
self.deferringUpdates = NO;
}
- (BOOL)deferringUpdates
{
return _deferringUpdates && [CLLocationManager deferredLocationUpdatesAvailable] &&
[UIApplication sharedApplication].applicationState == UIApplicationStateBackground;
}
- (void)processLocation:(CLLocation *)location
{
// According to documentation, lat and lon are valid only if horz acc is non-negative.
// So we filter out such events completely.
if (location.horizontalAccuracy < 0.)
return;
self.lastLocationError = location::TLocationError::ENoError;
// Save current device time for location.
self.lastLocationTime = [NSDate date];
[[Statistics instance] logLocation:location];
auto const newInfo = gpsInfoFromLocation(location);
if (!MapsAppDelegate.theApp.isDaemonMode)
{
for (id observer in self.observers.copy)
[observer onLocationUpdate:newInfo];
// TODO(AlexZ): Temporary, remove in the future.
}
GpsTracker::Instance().OnLocationUpdated(newInfo);
}
- (void)locationManager:(CLLocationManager *)manager didFinishDeferredUpdatesWithError:(NSError *)error
{
self.deferringUpdates = YES;
}
- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
{
NSLog(@"locationManager failed with error: %ld, %@", (long)error.code, error.description);
if (error.code == kCLErrorDenied)
{
if (kRequestAuthStatus == kCLAuthorizationStatusAuthorizedAlways && [self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)])
[Alohalytics logEvent:kAlohalyticsLocationRequestAlwaysFailed];
for (id observer in self.observers.copy)
[self observer:observer onLocationError:location::EDenied];
}
else if (error.code != kCLErrorLocationUnknown)
{
for (id observer in self.observers.copy)
[self observer:observer onLocationError:location::ENotSupported];
}
}
- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager
{
if (MapsAppDelegate.theApp.isDaemonMode)
return NO;
bool on = false;
settings::Get("CompassCalibrationEnabled", on);
if (!on)
return NO;
if (!MapsAppDelegate.theApp.mapViewController.controlsManager.searchHidden)
return NO;
if (!manager.heading)
return YES;
else if (manager.heading.headingAccuracy < 0)
return YES;
else if (manager.heading.headingAccuracy > 5)
return YES;
return NO;
}
- (bool)getLat:(double &)lat Lon:(double &)lon
{
CLLocation * l = [self lastLocation];
// Return last saved location if it's not later than 5 minutes.
if ([self lastLocationIsValid])
{
lat = l.coordinate.latitude;
lon = l.coordinate.longitude;
return true;
}
return false;
}
- (bool)getNorthRad:(double &)rad
{
CLHeading * h = [self lastHeading];
if (h != nil)
{
rad = (h.trueHeading < 0) ? h.magneticHeading : h.trueHeading;
rad = my::DegToRad(rad);
return true;
}
return false;
}
+ (NSString *)formattedDistance:(double)meters
{
if (meters < 0.)
return nil;
string s;
MeasurementUtils::FormatDistance(meters, s);
return @(s.c_str());
}
+ (char const *)getSpeedSymbol:(double)metersPerSecond
{
// 0-1 m/s
static char const * turtle = "\xF0\x9F\x90\xA2 ";
// 1-2 m/s
static char const * pedestrian = "\xF0\x9F\x9A\xB6 ";
// 2-5 m/s
static char const * tractor = "\xF0\x9F\x9A\x9C ";
// 5-10 m/s
static char const * bicycle = "\xF0\x9F\x9A\xB2 ";
// 10-36 m/s
static char const * car = "\xF0\x9F\x9A\x97 ";
// 36-120 m/s
static char const * train = "\xF0\x9F\x9A\x85 ";
// 120-278 m/s
static char const * airplane = "\xE2\x9C\x88\xEF\xB8\x8F ";
// 278+
static char const * rocket = "\xF0\x9F\x9A\x80 ";
if (metersPerSecond <= 1.) return turtle;
else if (metersPerSecond <= 2.) return pedestrian;
else if (metersPerSecond <= 5.) return tractor;
else if (metersPerSecond <= 10.) return bicycle;
else if (metersPerSecond <= 36.) return car;
else if (metersPerSecond <= 120.) return train;
else if (metersPerSecond <= 278.) return airplane;
else return rocket;
}
- (NSString *)formattedSpeedAndAltitude:(BOOL &)hasSpeed
{
hasSpeed = NO;
CLLocation * l = [self lastLocation];
if (l)
{
string result;
if (l.altitude)
result = "\xE2\x96\xB2 " /* this is simple mountain symbol */ + MeasurementUtils::FormatAltitude(l.altitude);
// Speed is actual only for just received location
if (l.speed > 0. && [l.timestamp timeIntervalSinceNow] >= -2.0)
{
hasSpeed = YES;
if (!result.empty())
result += " ";
result += [LocationManager getSpeedSymbol:l.speed] + MeasurementUtils::FormatSpeed(l.speed);
}
return result.empty() ? nil : @(result.c_str());
}
return nil;
}
- (bool)lastLocationIsValid
{
return (([self lastLocation] != nil) && ([self.lastLocationTime timeIntervalSinceNow] > -300.0));
}
- (bool)isLocationPendingOrNoPosition
{
using location::EMyPositionMode;
EMyPositionMode mode;
if (!settings::Get(settings::kLocationStateMode, mode))
return true;
return mode == EMyPositionMode::PendingPosition || mode == EMyPositionMode::NotFollowNoPosition;
}
- (BOOL)enabledOnMap
{
for (id observer in self.observers.copy)
if ([observer isKindOfClass:[MapViewController class]])
return YES;
return NO;
}
- (void)orientationChanged
{
self.locationManager.headingOrientation = (CLDeviceOrientation)[UIDevice currentDevice].orientation;
}
- (void)notifyCompassUpdate:(location::CompassInfo const &)newInfo
{
for (id observer in self.observers.copy)
if ([observer respondsToSelector:@selector(onCompassUpdate:)])
[observer onCompassUpdate:newInfo];
}
- (void)observer:(id<LocationObserver>)observer onLocationError:(location::TLocationError)errorCode
{
self.lastLocationError = errorCode;
if ([(NSObject *)observer respondsToSelector:@selector(onLocationError:)])
[observer onLocationError:errorCode];
}
location::GpsInfo gpsInfoFromLocation(CLLocation const * l)
{
location::GpsInfo info;
info.m_source = location::EAppleNative;
info.m_latitude = l.coordinate.latitude;
info.m_longitude = l.coordinate.longitude;
info.m_timestamp = [l.timestamp timeIntervalSince1970];
if (l.horizontalAccuracy >= 0.0)
info.m_horizontalAccuracy = l.horizontalAccuracy;
if (l.verticalAccuracy >= 0.0)
{
info.m_verticalAccuracy = l.verticalAccuracy;
info.m_altitude = l.altitude;
}
if (l.course >= 0.0)
info.m_bearing = l.course;
if (l.speed >= 0.0)
info.m_speed = l.speed;
return info;
}
location::CompassInfo compasInfoFromHeading(CLHeading const * h)
{
location::CompassInfo info;
if (h.trueHeading >= 0.0)
info.m_bearing = my::DegToRad(h.trueHeading);
else if (h.headingAccuracy >= 0.0)
info.m_bearing = my::DegToRad(h.magneticHeading);
return info;
}
- (void)reset
{
_isStarted = NO;
_lastLocationTime = nil;
_observers = [NSMutableSet set];
_locationManager.delegate = nil;
_locationManager = nil;
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)processMyPositionStateModeEvent:(location::EMyPositionMode)mode
{
auto const & f = GetFramework();
if (f.IsRoutingActive())
{
switch (f.GetRouter())
{
case routing::RouterType::Vehicle:
self.geoMode = GeoMode::VehicleRouting;
break;
case routing::RouterType::Pedestrian:
self.geoMode = GeoMode::PedestrianRouting;
break;
case routing::RouterType::Bicycle:
self.geoMode = GeoMode::BicycleRouting;
break;
}
}
else
{
switch (mode)
{
case location::EMyPositionMode::PendingPosition:
self.geoMode = GeoMode::Pending;
break;
case location::EMyPositionMode::NotFollowNoPosition:
case location::EMyPositionMode::NotFollow:
self.geoMode = GeoMode::NotInPosition;
break;
case location::EMyPositionMode::Follow:
self.geoMode = GeoMode::InPosition;
break;
case location::EMyPositionMode::FollowAndRotate:
self.geoMode = GeoMode::FollowAndRotate;
break;
}
}
[self refreshGeoModeSettings];
}
#pragma mark - Properties
- (CLLocationManager *)locationManager
{
if (!_locationManager)
{
[self reset];
_locationManager = [[CLLocationManager alloc] init];
_locationManager.delegate = self;
[UIDevice currentDevice].batteryMonitoringEnabled = YES;
[self refreshGeoModeSettings];
if (!(isIOS7 || isIOS8))
_locationManager.allowsBackgroundLocationUpdates = YES;
_locationManager.pausesLocationUpdatesAutomatically = YES;
_locationManager.headingFilter = 3.0;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientationChanged) name:UIDeviceOrientationDidChangeNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(batteryStateChangedNotification:) name:UIDeviceBatteryStateDidChangeNotification object:nil];
}
return _locationManager;
}
@end
@implementation CLLocation (Mercator)
- (m2::PointD)mercator
{
return ToMercator(self.coordinate);
}
@end