diff --git a/iphone/Maps/Classes/MapViewController.h b/iphone/Maps/Classes/MapViewController.h index 8c8c31ac68..2918525c02 100644 --- a/iphone/Maps/Classes/MapViewController.h +++ b/iphone/Maps/Classes/MapViewController.h @@ -1,4 +1,5 @@ -#import +#import +#import "LocationManager.h" #include "../../geometry/point2d.hpp" #include "../../yg/texture.hpp" @@ -8,7 +9,7 @@ #include "../../map/feature_vec_model.hpp" #include "../../std/shared_ptr.hpp" -@interface MapViewController : UIViewController +@interface MapViewController : UIViewController { enum Action { diff --git a/iphone/Maps/Classes/MapViewController.mm b/iphone/Maps/Classes/MapViewController.mm index c1bfccade9..1ba6e7148b 100644 --- a/iphone/Maps/Classes/MapViewController.mm +++ b/iphone/Maps/Classes/MapViewController.mm @@ -28,62 +28,71 @@ storage::Storage m_storage; m_framework->ShowRect(rect); } -- (void)startLocation +//******************************************************************************************** +//*********************** Callbacks from LocationManager ************************************* +- (void)onLocationStatusChanged:(location::TLocationStatus)newStatus { - typedef void (*OnLocationUpdatedFunc)(id, SEL, location::TLocationStatus); - SEL onLocUpdatedSel = @selector(OnLocationUpdated:); - OnLocationUpdatedFunc locUpdatedImpl = (OnLocationUpdatedFunc)[self methodForSelector:onLocUpdatedSel]; - - m_myPositionButton.selected = YES; - [m_myPositionButton setImage:[UIImage imageNamed:@"location-search.png"] forState:UIControlStateSelected]; - [[MapsAppDelegate theApp] disableStandby]; - - m_framework->StartLocationService(bind(locUpdatedImpl, self, onLocUpdatedSel, _1)); -} - -- (void)stopLocation -{ - m_myPositionButton.selected = NO; - [m_myPositionButton setImage:[UIImage imageNamed:@"location.png"] forState:UIControlStateSelected]; - [[MapsAppDelegate theApp] enableStandby]; - - m_framework->StopLocationService(); -} - -- (IBAction)OnMyPositionClicked:(id)sender -{ - if (m_myPositionButton.isSelected == NO) - [self startLocation]; - else - [self stopLocation]; -} - -- (void)OnLocationUpdated:(location::TLocationStatus) status -{ - switch (status) + m_framework->OnLocationStatusChanged(newStatus); + switch (newStatus) { case location::EDisabledByUser: { - UIAlertView * alert = [[[UIAlertView alloc] initWithTitle:@"Location Services are disabled" - message:@"You currently have all location services for this device or application disabled. Please, enable them in Settings->Location Services." - delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] autorelease]; + UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"Location Services are disabled" + message:@"You currently have all location services for this device or application disabled. Please, enable them in Settings Application->Location Services." + delegate:nil + cancelButtonTitle:@"Ok" + otherButtonTitles:nil]; [alert show]; - [self stopLocation]; + [alert release]; + [[MapsAppDelegate theApp].m_locationManager stop:self]; } break; case location::ENotSupported: { - UIAlertView * alert = [[[UIAlertView alloc] initWithTitle:@"Location Services are not supported" - message:@"Your device doesn't support location services" - delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] autorelease]; + UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"Location Services are not supported" + message:@"Your device doesn't support location services" + delegate:nil + cancelButtonTitle:@"Ok" + otherButtonTitles:nil]; [alert show]; - [self stopLocation]; + [alert release]; + [[MapsAppDelegate theApp].m_locationManager stop:self]; } break; - case location::ERoughMode: - case location::EAccurateMode: - [m_myPositionButton setImage:[UIImage imageNamed:@"location-selected.png"] forState:UIControlStateSelected]; + case location::EFirstEvent: + [m_myPositionButton setImage:[UIImage imageNamed:@"location-selected.png"] forState:UIControlStateSelected]; break; + default: + break; + } +} + +- (void)onGpsUpdate:(location::GpsInfo const &)info +{ + m_framework->OnGpsUpdate(info); +} + +- (void)onCompassUpdate:(location::CompassInfo const &)info +{ + m_framework->OnCompassUpdate(info); +} +//******************************************************************************************** +//******************************************************************************************** +- (IBAction)OnMyPositionClicked:(id)sender +{ + if (m_myPositionButton.isSelected == NO) + { + m_myPositionButton.selected = YES; + [m_myPositionButton setImage:[UIImage imageNamed:@"location-search.png"] forState:UIControlStateSelected]; + [[MapsAppDelegate theApp] disableStandby]; + [[MapsAppDelegate theApp].m_locationManager start:self]; + } + else + { + m_myPositionButton.selected = NO; + [m_myPositionButton setImage:[UIImage imageNamed:@"location.png"] forState:UIControlStateSelected]; + [[MapsAppDelegate theApp] enableStandby]; + [[MapsAppDelegate theApp].m_locationManager stop:self]; } } diff --git a/iphone/Maps/Classes/MapsAppDelegate.h b/iphone/Maps/Classes/MapsAppDelegate.h index 8d6204d4a8..7d838503ba 100644 --- a/iphone/Maps/Classes/MapsAppDelegate.h +++ b/iphone/Maps/Classes/MapsAppDelegate.h @@ -2,6 +2,7 @@ @class MapViewController; @class SettingsManager; +@class LocationManager; @interface MapsAppDelegate : NSObject { @@ -10,12 +11,15 @@ MapViewController * m_mapViewController; SettingsManager * m_settingsManager; NSInteger m_standbyCounter; + LocationManager * m_locationManager; } @property (nonatomic, retain) IBOutlet UINavigationController * m_navigationController; @property (nonatomic, retain) IBOutlet UIWindow * m_window; @property (nonatomic, retain) IBOutlet MapViewController * m_mapViewController; +@property (nonatomic, readonly) LocationManager * m_locationManager; + + (MapsAppDelegate *) theApp; - (SettingsManager *)settingsManager; diff --git a/iphone/Maps/Classes/MapsAppDelegate.mm b/iphone/Maps/Classes/MapsAppDelegate.mm index 858c3f1e98..1f12701074 100644 --- a/iphone/Maps/Classes/MapsAppDelegate.mm +++ b/iphone/Maps/Classes/MapsAppDelegate.mm @@ -2,12 +2,14 @@ #import "MapViewController.h" #import "SettingsManager.h" #import "Preferences.h" +#import "LocationManager.h" @implementation MapsAppDelegate @synthesize m_navigationController; @synthesize m_window; @synthesize m_mapViewController; +@synthesize m_locationManager; + (MapsAppDelegate *) theApp { @@ -32,6 +34,7 @@ - (void) applicationDidFinishLaunching: (UIApplication *) application { [Preferences setup:m_mapViewController]; + m_locationManager = [[LocationManager alloc] init]; [m_window addSubview:m_navigationController.view]; [m_window makeKeyAndVisible]; @@ -46,6 +49,7 @@ - (void) dealloc { + [m_locationManager release]; [m_settingsManager release]; m_mapViewController = nil; m_window = nil; diff --git a/iphone/Maps/Maps.xcodeproj/project.pbxproj b/iphone/Maps/Maps.xcodeproj/project.pbxproj index 95d76da7df..20fb203878 100644 --- a/iphone/Maps/Maps.xcodeproj/project.pbxproj +++ b/iphone/Maps/Maps.xcodeproj/project.pbxproj @@ -82,6 +82,7 @@ FA64D9A913F975AD00350ECF /* types.txt in Resources */ = {isa = PBXBuildFile; fileRef = FA64D9A813F975AD00350ECF /* types.txt */; }; FA87151B12B1518F00592DAF /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FA87151A12B1518F00592DAF /* SystemConfiguration.framework */; }; FA8F8938132D5DB00048E3FE /* libtomcrypt.a in Frameworks */ = {isa = PBXBuildFile; fileRef = FA8F8937132D5DB00048E3FE /* libtomcrypt.a */; }; + FAA5C2A2144F135F005337F6 /* LocationManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = FAA5C2A1144F135F005337F6 /* LocationManager.mm */; }; FAA7A73414055351009F76D8 /* location-selected.png in Resources */ = {isa = PBXBuildFile; fileRef = FAA7A73214055351009F76D8 /* location-selected.png */; }; FAA7A73514055351009F76D8 /* location-selected@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = FAA7A73314055351009F76D8 /* location-selected@2x.png */; }; FAAFD697139D9BE2000AE70C /* categories.txt in Resources */ = {isa = PBXBuildFile; fileRef = FAAFD696139D9BE2000AE70C /* categories.txt */; }; @@ -696,6 +697,8 @@ FA87151A12B1518F00592DAF /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; FA8F8937132D5DB00048E3FE /* libtomcrypt.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libtomcrypt.a; sourceTree = SOURCE_ROOT; }; FAA4B13E13EC1C8C00BCAB63 /* DiskFreeSpace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DiskFreeSpace.h; sourceTree = ""; }; + FAA5C2A0144F135F005337F6 /* LocationManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LocationManager.h; path = Platform/LocationManager.h; sourceTree = ""; }; + FAA5C2A1144F135F005337F6 /* LocationManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = LocationManager.mm; path = Platform/LocationManager.mm; sourceTree = ""; }; FAA7A73214055351009F76D8 /* location-selected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "location-selected.png"; sourceTree = ""; }; FAA7A73314055351009F76D8 /* location-selected@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "location-selected@2x.png"; sourceTree = ""; }; FAAE8D5D1338FF8B003ECAD5 /* GetActiveConnectionType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GetActiveConnectionType.h; sourceTree = ""; }; @@ -1457,6 +1460,8 @@ FAF37EFB126DCE6F005EA154 /* IPhoneDownloadManager.mm */, FAF37F03126DCF11005EA154 /* IPhoneDownload.h */, FAF37EFA126DCE6F005EA154 /* IPhoneDownload.mm */, + FAA5C2A0144F135F005337F6 /* LocationManager.h */, + FAA5C2A1144F135F005337F6 /* LocationManager.mm */, ); name = Platform; sourceTree = ""; @@ -2612,6 +2617,7 @@ FA09E01113F71F6C007E69CA /* SearchVC.mm in Sources */, FABF223E13FAA97A003D4D49 /* CompassView.mm in Sources */, FA29FDAA141E77F8004ADF66 /* Preferences.mm in Sources */, + FAA5C2A2144F135F005337F6 /* LocationManager.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/iphone/Maps/Platform/LocationManager.h b/iphone/Maps/Platform/LocationManager.h new file mode 100644 index 0000000000..f74383aa6d --- /dev/null +++ b/iphone/Maps/Platform/LocationManager.h @@ -0,0 +1,23 @@ +#import +#import + +#include "../../platform/location.hpp" + +@protocol LocationObserver +@required + - (void)onLocationStatusChanged:(location::TLocationStatus)newStatus; + - (void)onGpsUpdate:(location::GpsInfo const &)info; + - (void)onCompassUpdate:(location::CompassInfo const &)info; +@end + +@interface LocationManager : NSObject +{ + CLLocationManager * m_locationManager; + BOOL m_isStarted; + BOOL m_reportFirstUpdate; + NSMutableSet * m_observers; +} + +- (void)start:(id )observer; +- (void)stop:(id )observer; +@end diff --git a/iphone/Maps/Platform/LocationManager.mm b/iphone/Maps/Platform/LocationManager.mm new file mode 100644 index 0000000000..d8251d57d9 --- /dev/null +++ b/iphone/Maps/Platform/LocationManager.mm @@ -0,0 +1,135 @@ +#import "LocationManager.h" +#import + +@implementation LocationManager + +- (id)init +{ + if (self = [super init]) + { + m_locationManager = [[CLLocationManager alloc] init]; + m_locationManager.delegate = self; + m_locationManager.purpose = @"Location services are needed to display your current position on the map."; + m_locationManager.desiredAccuracy = kCLLocationAccuracyBest; + m_locationManager.headingFilter = 3.0; + m_locationManager.distanceFilter = 1.0; + m_isStarted = NO; + m_reportFirstUpdate = YES; + m_observers = [[NSMutableSet alloc] init]; + } + return self; +} + +- (void)dealloc +{ + [m_observers release]; + [m_locationManager release]; + [super dealloc]; +} + +- (void)start:(id )observer +{ + if (!m_isStarted) + { + if ([CLLocationManager locationServicesEnabled]) + { + switch([CLLocationManager authorizationStatus]) + { + case kCLAuthorizationStatusAuthorized: + case kCLAuthorizationStatusNotDetermined: + [m_locationManager startUpdatingLocation]; + if ([CLLocationManager headingAvailable]) + [m_locationManager startUpdatingHeading]; + m_isStarted = YES; + [m_observers addObject:observer]; + [observer onLocationStatusChanged:location::EStarted]; + break; + case kCLAuthorizationStatusRestricted: + case kCLAuthorizationStatusDenied: + [observer onLocationStatusChanged:location::EDisabledByUser]; + break; + } + } + else + [observer onLocationStatusChanged:location::ENotSupported]; + } + else + [observer onLocationStatusChanged:location::EStarted]; +} + +- (void)stop:(id )observer +{ + if (m_isStarted) + { + [m_observers removeObject:observer]; + if ([m_observers count] == 0) + { // stop only if no more observers are subsribed + m_isStarted = NO; + m_reportFirstUpdate = YES; + if ([CLLocationManager headingAvailable]) + [m_locationManager stopUpdatingHeading]; + [m_locationManager stopUpdatingLocation]; + } + } + [observer onLocationStatusChanged:location::EStopped]; +} + +- (void)location:(CLLocation *)location toGpsInfo:(location::GpsInfo &)info +{ + info.m_horizontalAccuracy = location.horizontalAccuracy; + info.m_latitude = location.coordinate.latitude; + info.m_longitude = location.coordinate.longitude; + info.m_timestamp = [location.timestamp timeIntervalSince1970]; + info.m_source = location::EAppleNative; +// info.m_verticalAccuracy = location.verticalAccuracy; +// info.m_altitude = location.altitude; +// info.m_course = location.course; +// info.m_speed = location.speed; +} + +- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation +{ + if (m_reportFirstUpdate) + { + for (id observer in m_observers) + [observer onLocationStatusChanged:location::EFirstEvent]; + m_reportFirstUpdate = NO; + } + + location::GpsInfo newInfo; + [self location:newLocation toGpsInfo:newInfo]; + for (id observer in m_observers) + [observer onGpsUpdate:newInfo]; +} + +- (void)locationManager:(CLLocationManager *)manager didUpdateHeading:(CLHeading *)newHeading +{ + location::CompassInfo newInfo; + newInfo.m_magneticHeading = newHeading.magneticHeading; + newInfo.m_trueHeading = newHeading.trueHeading; + newInfo.m_accuracy = newHeading.headingAccuracy; +// newInfo.m_x = newHeading.x; +// newInfo.m_y = newHeading.y; +// newInfo.m_z = newHeading.z; + newInfo.m_timestamp = [newHeading.timestamp timeIntervalSince1970]; + for (id observer in m_observers) + [observer onCompassUpdate:newInfo]; +} + +- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error +{ + NSLog(@"locationManager failed with error: %d, %@", error.code, error.description); + if (error.code == kCLErrorDenied) + { + for (id observer in m_observers) + [observer onLocationStatusChanged:location::EDisabledByUser]; + } +} + +// Display compass calibration dialog automatically +- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager +{ + return YES; +} + +@end diff --git a/map/framework.cpp b/map/framework.cpp index 425d44a837..8b423ef15d 100644 --- a/map/framework.cpp +++ b/map/framework.cpp @@ -1,5 +1,3 @@ -#include "../base/SRC_FIRST.hpp" - #include "framework.hpp" #include "draw_processor.hpp" #include "drawer_yg.hpp" @@ -26,6 +24,7 @@ #include "../std/algorithm.hpp" #include "../std/fstream.hpp" +#include "../std/target_os.hpp" #include "render_policy_st.hpp" #include "render_policy_mt.hpp" @@ -60,26 +59,33 @@ void Framework::RemoveMap(string const & datFile) } template -void Framework::OnGpsUpdate(location::GpsInfo const & info) +void Framework::OnLocationStatusChanged(location::TLocationStatus newStatus) { - // notify GUI (note that gps can be disabled by user or even not available) - if (!(m_locationState & location::State::EGps) && m_locationObserver) - m_locationObserver(info.m_status); - - if (info.m_status == location::EAccurateMode || info.m_status == location::ERoughMode) + if (newStatus == location::EStarted) + // reset centering mode + m_centeringMode = ECenterAndScale; + else { - m_locationState.UpdateGps(info); - if (m_centeringMode == ECenterAndScale) - { - CenterAndScaleViewport(); - m_centeringMode = ECenterOnly; - } - else if (m_centeringMode == ECenterOnly) - SetViewportCenter(m_locationState.Position()); + m_centeringMode = EDoNothing; + m_locationState.TurnOff(); Invalidate(); } } +template +void Framework::OnGpsUpdate(location::GpsInfo const & info) +{ + m_locationState.UpdateGps(info); + if (m_centeringMode == ECenterAndScale) + { + CenterAndScaleViewport(); + m_centeringMode = ECenterOnly; + } + else if (m_centeringMode == ECenterOnly) + SetViewportCenter(m_locationState.Position()); + Invalidate(); +} + template void Framework::OnCompassUpdate(location::CompassInfo const & info) { @@ -134,12 +140,6 @@ Framework::Framework(shared_ptr windowHandle, m_informationDisplay.enableLog(isVisualLogEnabled, m_windowHandle.get()); m_informationDisplay.setVisualScale(visScale); - // initialize gps and compass subsystem - GetLocationManager().SetGpsObserver( - bind(&this_type::OnGpsUpdate, this, _1)); - GetLocationManager().SetCompassObserver( - bind(&this_type::OnCompassUpdate, this, _1)); - // set language priorities languages::CodesT langCodes; languages::GetCurrentSettings(langCodes); @@ -199,26 +199,6 @@ TModel & Framework::get_model() return m_model; } -template -void Framework::StartLocationService(LocationRetrievedCallbackT observer) -{ - m_locationObserver = observer; - m_centeringMode = ECenterAndScale; - // by default, we always start in accurate mode - GetLocationManager().StartUpdate(true); -} - -template -void Framework::StopLocationService() -{ - // reset callback - m_locationObserver.clear(); - m_centeringMode = EDoNothing; - GetLocationManager().StopUpdate(); - m_locationState.TurnOff(); - Invalidate(); -} - template bool Framework::IsEmptyModel() { diff --git a/map/framework.hpp b/map/framework.hpp index 5626450fb6..48660f3edc 100644 --- a/map/framework.hpp +++ b/map/framework.hpp @@ -52,8 +52,6 @@ struct BenchmarkRectProvider; namespace search { class Result; } typedef function SearchCallbackT; -typedef function LocationRetrievedCallbackT; - class DrawerYG; class RenderPolicy; @@ -118,7 +116,6 @@ protected: }; TGpsCenteringMode m_centeringMode; - LocationRetrievedCallbackT m_locationObserver; location::State m_locationState; mutable threads::Mutex m_modelSyn; @@ -133,15 +130,13 @@ protected: void RemoveMap(string const & datFile); public: - void OnGpsUpdate(location::GpsInfo const & info); - - void OnCompassUpdate(location::CompassInfo const & info); - - Framework(shared_ptr windowHandle, - size_t bottomShift); - + Framework(shared_ptr windowHandle, size_t bottomShift); virtual ~Framework(); + void OnLocationStatusChanged(location::TLocationStatus newStatus); + void OnGpsUpdate(location::GpsInfo const & info); + void OnCompassUpdate(location::CompassInfo const & info); + void SetRenderPolicy(shared_ptr const & rp); void InitializeGL(shared_ptr const & primaryContext, @@ -171,9 +166,6 @@ public: LOG(LINFO, ("Storage initialized")); } - void StartLocationService(LocationRetrievedCallbackT observer); - void StopLocationService(); - bool IsEmptyModel(); // Cleanup. diff --git a/map/location_state.cpp b/map/location_state.cpp index af0aca99b4..b874fcf5db 100644 --- a/map/location_state.cpp +++ b/map/location_state.cpp @@ -14,27 +14,14 @@ namespace location void State::UpdateGps(GpsInfo const & info) { - if (info.m_status == EAccurateMode - || info.m_status == ERoughMode) - { - m_flags |= EGps; - if (info.m_status == EAccurateMode) - m_flags |= EPreciseMode; - else - m_flags &= !EPreciseMode; - - m_positionMercator = m2::PointD(MercatorBounds::LonToX(info.m_longitude), - MercatorBounds::LatToY(info.m_latitude)); - m2::RectD const errorRectXY = - MercatorBounds::MetresToXY(info.m_longitude, info.m_latitude, - info.m_horizontalAccuracy); - m_errorRadiusMercator = sqrt((errorRectXY.SizeX() * errorRectXY.SizeX() - + errorRectXY.SizeY() * errorRectXY.SizeY()) / 4); - } - else - { - m_flags &= !EGps; - } + m_flags |= EGps; + m_positionMercator = m2::PointD(MercatorBounds::LonToX(info.m_longitude), + MercatorBounds::LatToY(info.m_latitude)); + m2::RectD const errorRectXY = + MercatorBounds::MetresToXY(info.m_longitude, info.m_latitude, + info.m_horizontalAccuracy); + m_errorRadiusMercator = sqrt((errorRectXY.SizeX() * errorRectXY.SizeX() + + errorRectXY.SizeY() * errorRectXY.SizeY()) / 4); } void State::UpdateCompass(CompassInfo const & info) @@ -84,7 +71,7 @@ namespace location drawer.drawSymbol(pxPosition, "current-position", yg::EPosCenter, yg::maxDepth); // my position circle drawer.screen()->fillSector(pxPosition, 0, math::pi * 2, pxErrorRadius, - yg::Color(0, 0, 255, (m_flags & State::EPreciseMode) ? 32 : 16), + yg::Color(0, 0, 255, 32), yg::maxDepth - 3); // display compass only if position is available if (m_flags & State::ECompass) diff --git a/map/location_state.hpp b/map/location_state.hpp index ff58060602..416cc47fcb 100644 --- a/map/location_state.hpp +++ b/map/location_state.hpp @@ -24,8 +24,7 @@ namespace location { ENone = 0x0, EGps = 0x1, - EPreciseMode = 0x2, - ECompass = 0x4, + ECompass = 0x2 }; State(); diff --git a/platform/apple_location_service.mm b/platform/apple_location_service.mm index 24ddc14bb8..4a28b14385 100644 --- a/platform/apple_location_service.mm +++ b/platform/apple_location_service.mm @@ -1,4 +1,4 @@ -#include "location.hpp" +#include "location_service.hpp" #include "../std/target_os.hpp" @@ -23,89 +23,60 @@ class AppleLocationService : public LocationService CLLocationManager * m_locationManager; public: - AppleLocationService() + AppleLocationService(LocationObserver & observer) : LocationService(observer) { m_objCppWrapper = [[LocationManagerWrapper alloc] initWithService:this]; m_locationManager = [[CLLocationManager alloc] init]; m_locationManager.delegate = m_objCppWrapper; + m_locationManager.desiredAccuracy = kCLLocationAccuracyBest; + m_locationManager.purpose = @"Location services are needed to display your current position on the map."; } - ~AppleLocationService() + virtual ~AppleLocationService() { [m_locationManager release]; [m_objCppWrapper release]; } - void OnLocationUpdate(GpsInfo & newLocation) + void OnLocationUpdate(GpsInfo const & info) { - NotifyGpsObserver(newLocation); + m_observer.OnGpsUpdated(info); } - void OnHeadingUpdate(CompassInfo & newHeading) + void OnDeniedError() { - NotifyCompassObserver(newHeading); + m_observer.OnLocationStatusChanged(location::EDisabledByUser); } -// virtual bool IsServiceSupported() -// { -// // Mac OS 10.6+ and iOS 4.0+ support this definitely -// return true; -// } - -// virtual bool IsServiceEnabled() -// { -// return [CLLocationManager locationServicesEnabled]; -// } - -// virtual bool IsCompassAvailable() -// { -//#ifdef OMIM_OS_MAC -// return false; -//#else // iOS 4.0+ have it -// return [CLLocationManager headingAvailable]; -//#endif -// } - - virtual void StartUpdate(bool useAccurateMode) + virtual void Start() { if (![CLLocationManager locationServicesEnabled]) { - GpsInfo info; - info.m_status = EDisabledByUser; - info.m_source = location::EAppleNative; - info.m_timestamp = [[NSDate date] timeIntervalSince1970]; - NotifyGpsObserver(info); + // @TODO correctly handle situation in GUI when wifi is working and native is disabled + // m_observer.OnLocationStatusChanged(location::ENotSupported); } else { - if (useAccurateMode) - m_locationManager.desiredAccuracy = kCLLocationAccuracyBest; - else - m_locationManager.desiredAccuracy = ROUGH_ACCURACY; - [m_locationManager startUpdatingLocation]; - // enable compass -#ifdef OMIM_OS_IPHONE - if ([CLLocationManager headingAvailable]) + switch([CLLocationManager authorizationStatus]) { - m_locationManager.headingFilter = 1.0; - [m_locationManager startUpdatingHeading]; + case kCLAuthorizationStatusAuthorized: + case kCLAuthorizationStatusNotDetermined: + [m_locationManager startUpdatingLocation]; + m_observer.OnLocationStatusChanged(location::EStarted); + break; + case kCLAuthorizationStatusRestricted: + case kCLAuthorizationStatusDenied: + // @TODO correctly handle situation in GUI when wifi is working and native is disabled + //m_observer.OnLocationStatusChanged(location::EDisabledByUser); + break; } -#endif } } - virtual void StopUpdate() + virtual void Stop() { -#ifdef OMIM_OS_IPHONE - if ([CLLocationManager headingAvailable]) - [m_locationManager stopUpdatingHeading]; -#endif [m_locationManager stopUpdatingLocation]; - } - - bool IsAccurateMode() const - { - return m_locationManager.desiredAccuracy == kCLLocationAccuracyBest; + m_observer.OnLocationStatusChanged(location::EStopped); } }; @@ -113,29 +84,22 @@ public: - (id)initWithService:(AppleLocationService *) service { - self = [super init]; - if (self) { + if (self = [super init]) m_service = service; - } return self; } -- (void)dealloc -{ - [super dealloc]; -} - + (void)location:(CLLocation *)location toGpsInfo:(GpsInfo &) info { - info.m_altitude = location.altitude; - info.m_course = location.course; - info.m_speed = location.speed; info.m_horizontalAccuracy = location.horizontalAccuracy; - info.m_verticalAccuracy = location.verticalAccuracy; info.m_latitude = location.coordinate.latitude; info.m_longitude = location.coordinate.longitude; info.m_timestamp = [location.timestamp timeIntervalSince1970]; info.m_source = location::EAppleNative; + //info.m_verticalAccuracy = location.verticalAccuracy; + //info.m_altitude = location.altitude; + //info.m_course = location.course; + //info.m_speed = location.speed; } - (void)locationManager:(CLLocationManager *)manager @@ -144,49 +108,20 @@ public: { GpsInfo newInfo; [LocationManagerWrapper location:newLocation toGpsInfo:newInfo]; - newInfo.m_status = m_service->IsAccurateMode() ? EAccurateMode : ERoughMode; m_service->OnLocationUpdate(newInfo); } -#ifdef OMIM_OS_IPHONE -- (void)locationManager:(CLLocationManager *)manager - didUpdateHeading:(CLHeading *)newHeading -{ - CompassInfo newInfo; - newInfo.m_magneticHeading = newHeading.magneticHeading; - newInfo.m_trueHeading = newHeading.trueHeading; - newInfo.m_accuracy = newHeading.headingAccuracy; - newInfo.m_x = newHeading.x; - newInfo.m_y = newHeading.y; - newInfo.m_z = newHeading.z; - newInfo.m_timestamp = [newHeading.timestamp timeIntervalSince1970]; - m_service->OnHeadingUpdate(newInfo); -} -#endif - - (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error { NSLog(@"locationManager failed with error: %ld, %@", error.code, error.description); if (error.code == kCLErrorDenied) - { - GpsInfo info; - info.m_status = EDisabledByUser; - info.m_source = location::EAppleNative; - info.m_timestamp = [[NSDate date] timeIntervalSince1970]; - m_service->OnLocationUpdate(info); - } -} - -// Display compass calibration dialog automatically -- (BOOL)locationManagerShouldDisplayHeadingCalibration:(CLLocationManager *)manager -{ - return YES; + m_service->OnDeniedError(); } @end -location::LocationService * CreateAppleLocationService() +extern "C" location::LocationService * CreateAppleLocationService(LocationObserver & observer) { - return new AppleLocationService(); + return new AppleLocationService(observer); } diff --git a/platform/location.hpp b/platform/location.hpp index 76008bf526..7342298854 100644 --- a/platform/location.hpp +++ b/platform/location.hpp @@ -11,11 +11,13 @@ namespace location enum TLocationStatus { - ENotSupported, //!< GpsInfo fields are not valid with this value - EDisabledByUser, //!< GpsInfo fields are not valid with this value - EAccurateMode, - ERoughMode //!< in this mode compass is turned off + EStopped, + EStarted, + EFirstEvent, //!< Sent when first valid coorinate is received + ENotSupported, + EDisabledByUser }; + enum TLocationSource { EAppleNative, @@ -28,15 +30,14 @@ namespace location { public: TLocationSource m_source; - TLocationStatus m_status; double m_timestamp; //!< seconds from 1st Jan 1970 double m_latitude; //!< degrees double m_longitude; //!< degrees double m_horizontalAccuracy; //!< metres - double m_altitude; //!< metres - double m_verticalAccuracy; //!< metres - double m_course; //!< positive degrees from the true North - double m_speed; //!< metres per second +// double m_altitude; //!< metres +// double m_verticalAccuracy; //!< metres +// double m_course; //!< positive degrees from the true North +// double m_speed; //!< metres per second }; /// @note always check m_status before using this structure @@ -47,52 +48,8 @@ namespace location double m_magneticHeading; //!< positive degrees from the magnetic North double m_trueHeading; //!< positive degrees from the true North double m_accuracy; //!< offset from magnetic to true North - int m_x; - int m_y; - int m_z; +// int m_x; +// int m_y; +// int m_z; }; - - typedef function TGpsCallback; - typedef function TCompassCallback; - - class LocationService - { - TGpsCallback m_gpsObserver; - TCompassCallback m_compassObserver; - - protected: - void NotifyGpsObserver(GpsInfo const & info) - { - if (m_gpsObserver) - m_gpsObserver(info); - } - void NotifyCompassObserver(CompassInfo const & info) - { - if (m_compassObserver) - m_compassObserver(info); - } - - public: - virtual ~LocationService() {} - - void SetGpsObserver(TGpsCallback observer) - { - m_gpsObserver = observer; - } - - void SetCompassObserver(TCompassCallback observer) - { - m_compassObserver = observer; - } - - /// to change active accuracy mode just call it again - /// @note also enables compass if it's available - virtual void StartUpdate(bool useAccurateMode) = 0; - virtual void StopUpdate() = 0; - }; - -} - -extern "C" location::LocationService & GetLocationManager(); -extern "C" location::LocationService * CreateAppleLocationService(); -extern "C" location::LocationService * CreateWiFiLocationService(); +} // namespace location diff --git a/platform/location_manager.cpp b/platform/location_service.cpp similarity index 55% rename from platform/location_manager.cpp rename to platform/location_service.cpp index 969242d848..9ecd552b7c 100644 --- a/platform/location_manager.cpp +++ b/platform/location_service.cpp @@ -1,12 +1,13 @@ -#include "location.hpp" +#include "location_service.hpp" #include "../std/target_os.hpp" #include "../std/vector.hpp" -#include "../std/bind.hpp" #include "../std/ctime.hpp" +namespace +{ -double ApproxDistanceSquareInMetres(double lat1, double lon1, double lat2, double lon2) +static double ApproxDistanceSquareInMetres(double lat1, double lon1, double lat2, double lon2) { double const m1 = (lat1 - lat2) / 111111.; double const m2 = (lon1 - lon2) / 111111.; @@ -42,69 +43,74 @@ public: } else m_prevLocation = new location::GpsInfo(newLocation); - return true; + return passes; } }; +} // namespace + +extern "C" location::LocationService * CreateAppleLocationService(location::LocationObserver &); +extern "C" location::LocationService * CreateWiFiLocationService(location::LocationObserver &); + namespace location { - class LocationManager : public LocationService + class DesktopLocationService : public LocationService, public LocationObserver { vector m_services; PositionFilter m_filter; + bool m_reportFirstEvent; - void OnGpsUpdate(GpsInfo const & info) + virtual void OnLocationStatusChanged(location::TLocationStatus newStatus) { - if (m_filter.Passes(info)) - NotifyGpsObserver(info); + m_observer.OnLocationStatusChanged(newStatus); } - void OnCompassUpdate(CompassInfo const & info) + virtual void OnGpsUpdated(GpsInfo const & info) { - NotifyCompassObserver(info); + if (m_reportFirstEvent) + { + m_observer.OnLocationStatusChanged(location::EFirstEvent); + m_reportFirstEvent = false; + } + if (m_filter.Passes(info)) + m_observer.OnGpsUpdated(info); } public: - LocationManager() + DesktopLocationService(LocationObserver & observer) + : LocationService(observer), m_reportFirstEvent(true) { - LocationService * service; - -#if defined(OMIM_OS_IPHONE) || defined(OMIM_OS_MAC) - service = CreateAppleLocationService(); - service->SetGpsObserver(bind(&LocationManager::OnGpsUpdate, this, _1)); - service->SetCompassObserver(bind(&LocationManager::OnCompassUpdate, this, _1)); - m_services.push_back(service); +#if defined(OMIM_OS_MAC) + m_services.push_back(CreateAppleLocationService(*this)); #endif -#if defined(OMIM_OS_WINDOWS) || defined(OMIM_OS_MAC) - service = CreateWiFiLocationService(); - service->SetGpsObserver(bind(&LocationManager::OnGpsUpdate, this, _1)); - m_services.push_back(service); +#if defined(OMIM_OS_DESKTOP) + m_services.push_back(CreateWiFiLocationService(*this)); #endif } - virtual ~LocationManager() + virtual ~DesktopLocationService() { for (size_t i = 0; i < m_services.size(); ++i) delete m_services[i]; } - virtual void StartUpdate(bool useAccurateMode) + virtual void Start() { for (size_t i = 0; i < m_services.size(); ++i) - m_services[i]->StartUpdate(useAccurateMode); + m_services[i]->Start(); } - virtual void StopUpdate() + virtual void Stop() { for (size_t i = 0; i < m_services.size(); ++i) - m_services[i]->StopUpdate(); + m_services[i]->Stop(); + m_reportFirstEvent = true; } }; } -location::LocationService & GetLocationManager() +location::LocationService * CreateDesktopLocationService(location::LocationObserver & observer) { - static location::LocationManager mgr; - return mgr; + return new location::DesktopLocationService(observer); } diff --git a/platform/location_service.hpp b/platform/location_service.hpp new file mode 100644 index 0000000000..ae66d3d5d3 --- /dev/null +++ b/platform/location_service.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include "location.hpp" + +namespace location +{ + +class LocationObserver +{ +public: + virtual void OnLocationStatusChanged(TLocationStatus newStatus) = 0; + virtual void OnGpsUpdated(GpsInfo const & info) = 0; +}; + +class LocationService +{ +protected: + LocationObserver & m_observer; + +public: + LocationService(LocationObserver & observer) : m_observer(observer) {} + virtual ~LocationService() {} + + virtual void Start() = 0; + virtual void Stop() = 0; +}; + +} // namespace location + +extern "C" location::LocationService * CreateDesktopLocationService(location::LocationObserver & observer); diff --git a/platform/platform.pro b/platform/platform.pro index 74ba8b35f3..495d672a96 100644 --- a/platform/platform.pro +++ b/platform/platform.pro @@ -18,17 +18,20 @@ QT *= core network wifi_location_service.cpp \ qt_download_manager.cpp \ qt_download.cpp \ - qt_concurrent_runner.cpp + qt_concurrent_runner.cpp \ + location_service.cpp HEADERS += qt_download_manager.hpp \ qt_download.hpp \ - wifi_info.hpp + wifi_info.hpp \ + location_service.hpp win32* { SOURCES += platform_win.cpp \ wifi_info_windows.cpp } else:macx* { OBJECTIVE_SOURCES += platform_mac.mm \ wifi_info_mac.mm \ - apple_video_timer.mm + apple_video_timer.mm \ + apple_location_service.mm } else:linux* { SOURCES += platform_linux.cpp } @@ -40,10 +43,6 @@ QT *= core network SOURCES += platform_android.cpp } -macx|iphone* { - OBJECTIVE_SOURCES += apple_location_service.mm -} - # common sources for all platforms HEADERS += \ @@ -58,7 +57,6 @@ HEADERS += \ url_generator.hpp \ SOURCES += \ - location_manager.cpp \ preferred_languages.cpp \ settings.cpp \ video_timer.cpp \ diff --git a/platform/wifi_location_service.cpp b/platform/wifi_location_service.cpp index eba5f5d6eb..20aa87a6b9 100644 --- a/platform/wifi_location_service.cpp +++ b/platform/wifi_location_service.cpp @@ -1,4 +1,4 @@ -#include "location.hpp" +#include "location_service.hpp" #include "wifi_info.hpp" #include "download_manager.hpp" @@ -46,10 +46,9 @@ namespace location info.m_longitude = json_real_value(lon); info.m_horizontalAccuracy = json_real_value(acc); // @TODO introduce flags to mark valid values - info.m_status = EAccurateMode; info.m_timestamp = static_cast(time(NULL)); info.m_source = location::EGoogle; - NotifyGpsObserver(info); + m_observer.OnGpsUpdated(info); return; } } @@ -91,21 +90,25 @@ namespace location } public: - virtual void StartUpdate(bool) + WiFiLocationService(LocationObserver & observer) : LocationService(observer) + { + } + + virtual void Start() { m_wifiInfo.RequestWiFiBSSIDs(bind(&WiFiLocationService::OnWifiScanCompleted, this, _1)); } - virtual void StopUpdate() + virtual void Stop() { m_wifiInfo.Stop(); } }; } -location::LocationService * CreateWiFiLocationService() +extern "C" location::LocationService * CreateWiFiLocationService(location::LocationObserver & observer) { // small hack - create and initialize downloader in main thread GetDownloadManager(); - return new location::WiFiLocationService(); + return new location::WiFiLocationService(observer); } diff --git a/qt/draw_widget.cpp b/qt/draw_widget.cpp index ff006d4535..f607d4a634 100644 --- a/qt/draw_widget.cpp +++ b/qt/draw_widget.cpp @@ -88,16 +88,6 @@ namespace qt // m_framework.ShowFeature(p); //} - void DrawWidget::OnEnableMyPosition(LocationRetrievedCallbackT observer) - { - m_framework->StartLocationService(observer); - } - - void DrawWidget::OnDisableMyPosition() - { - m_framework->StopLocationService(); - } - void DrawWidget::MoveLeft() { m_framework->Move(math::pi, 0.5); diff --git a/qt/draw_widget.hpp b/qt/draw_widget.hpp index 5e95e9ef93..f93a0340ed 100644 --- a/qt/draw_widget.hpp +++ b/qt/draw_widget.hpp @@ -9,7 +9,7 @@ #include "../platform/video_timer.hpp" -#include "../std/auto_ptr.hpp" +#include "../std/scoped_ptr.hpp" #include @@ -38,9 +38,9 @@ namespace qt typedef model::FeaturesFetcher model_t; - auto_ptr > m_framework; + scoped_ptr > m_framework; - auto_ptr m_videoTimer; + scoped_ptr m_videoTimer; bool m_isDrag; bool m_isRotate; @@ -76,9 +76,6 @@ namespace qt void SetScaleControl(QScaleSlider * pScale); - void OnEnableMyPosition(LocationRetrievedCallbackT observer); - void OnDisableMyPosition(); - void Search(string const & text, SearchCallbackT callback); void ShowFeature(m2::RectD const & rect); @@ -90,6 +87,8 @@ namespace qt void PrepareShutdown(); + Framework & Framework() { return *m_framework.get(); } + protected: static const uint32_t ini_file_version = 0; diff --git a/qt/mainwindow.cpp b/qt/mainwindow.cpp index 1afa271fc9..76fe7497f7 100644 --- a/qt/mainwindow.cpp +++ b/qt/mainwindow.cpp @@ -43,6 +43,7 @@ MainWindow::MainWindow() #endif // NO_DOWNLOADER { m_pDrawWidget = new DrawWidget(this, m_storage); + m_locationService.reset(CreateDesktopLocationService(*this)); CreateNavigationBar(); CreateSearchBarAndPanel(); @@ -301,10 +302,27 @@ void MainWindow::OnAbout() dlg.exec(); } -void MainWindow::OnLocationFound() +void MainWindow::OnLocationStatusChanged(location::TLocationStatus newStatus) { - m_pMyPositionAction->setIcon(QIcon(":/navig64/location.png")); - m_pMyPositionAction->setToolTip(tr("My Position")); + switch (newStatus) + { + case location::EFirstEvent: + m_pMyPositionAction->setIcon(QIcon(":/navig64/location.png")); + m_pMyPositionAction->setToolTip(tr("My Position")); + break; + case location::EDisabledByUser: + case location::ENotSupported: + m_pMyPositionAction->setChecked(false); + break; + default: + break; + } + m_pDrawWidget->Framework().OnLocationStatusChanged(newStatus); +} + +void MainWindow::OnGpsUpdated(location::GpsInfo const & info) +{ + m_pDrawWidget->Framework().OnGpsUpdate(info); } void MainWindow::OnMyPosition() @@ -313,13 +331,13 @@ void MainWindow::OnMyPosition() { m_pMyPositionAction->setIcon(QIcon(":/navig64/location-search.png")); m_pMyPositionAction->setToolTip(tr("Looking for position...")); - m_pDrawWidget->OnEnableMyPosition(bind(&MainWindow::OnLocationFound, this)); + m_locationService->Start(); } else { m_pMyPositionAction->setIcon(QIcon(":/navig64/location.png")); m_pMyPositionAction->setToolTip(tr("My Position")); - m_pDrawWidget->OnDisableMyPosition(); + m_locationService->Stop(); } } diff --git a/qt/mainwindow.hpp b/qt/mainwindow.hpp index a2569fcc86..dd1b9460c9 100644 --- a/qt/mainwindow.hpp +++ b/qt/mainwindow.hpp @@ -2,6 +2,10 @@ #include "../storage/storage.hpp" +#include "../platform/location_service.hpp" + +#include "../std/scoped_ptr.hpp" + #include class QDockWidget; @@ -13,7 +17,7 @@ namespace qt class DrawWidget; class UpdateDialog; - class MainWindow : public QMainWindow + class MainWindow : public QMainWindow, location::LocationObserver { QAction * m_pMyPositionAction; QAction * m_pSearchAction; @@ -27,20 +31,22 @@ namespace qt storage::Storage m_storage; + scoped_ptr m_locationService; + Q_OBJECT public: MainWindow(); virtual ~MainWindow(); + virtual void OnLocationStatusChanged(location::TLocationStatus newStatus); + virtual void OnGpsUpdated(location::GpsInfo const & info); + protected: string GetIniFile(); void SaveState(); void LoadState(); - private: - void OnLocationFound(); - protected: #ifndef NO_DOWNLOADER void CreateClassifPanel();