Merge pull request #1036 from mapbox/1033-metrics

Integrate Metrics
This commit is contained in:
Brad Leege
2015-03-24 17:07:00 -05:00
8 changed files with 647 additions and 1 deletions

View File

@@ -17,9 +17,13 @@
'../platform/darwin/image.mm',
'../platform/darwin/reachability.m',
'../include/mbgl/ios/MapboxGL.h',
'../include/mbgl/ios/MGLMapboxEvents.h',
'../platform/ios/MGLMapboxEvents.m',
'../include/mbgl/ios/MGLMapView.h',
'../platform/ios/MGLMapView.mm',
'../include/mbgl/ios/MGLAnnotation.h',
'../include/mbgl/ios/MGLMetricsLocationManager.h',
'../platform/ios/MGLMetricsLocationManager.m',
'../include/mbgl/ios/MGLStyleFunctionValue.h',
'../platform/ios/MGLStyleFunctionValue.m',
'../include/mbgl/ios/MGLTypes.h',
@@ -43,6 +47,7 @@
'ldflags': [
'-framework CoreGraphics',
'-framework CoreLocation',
'-framework CoreTelephony',
'-framework ImageIO',
'-framework GLKit',
'-framework MobileCoreServices',

View File

@@ -0,0 +1,26 @@
//
// MapboxEvents.h
// MapboxEvents
//
// Created by Brad Leege on 3/5/15.
// Copyright (c) 2015 Mapbox. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface MGLMapboxEvents : NSObject
@property (atomic) NSInteger flushAt;
@property (atomic) NSInteger flushAfter;
@property (atomic) NSString *api;
@property (atomic) NSString *token;
@property (atomic) NSString *appName;
@property (atomic) NSString *appVersion;
+ (id)sharedManager;
- (void) pushEvent:(NSString *)event withAttributes:(NSDictionary *)attributeDictionary;
- (void) flush;
@end

View File

@@ -0,0 +1,22 @@
//
// MBLocationManager.h
// Hermes
//
// Created by Brad Leege on 3/8/15.
// Copyright (c) 2015 Mapbox. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "CoreLocation/CoreLocation.h"
@interface MGLMetricsLocationManager : NSObject <CLLocationManagerDelegate>
+ (id)sharedManager;
- (BOOL) isAuthorizedStatusDetermined;
- (void) requestAlwaysAuthorization;
- (void) startUpdatingLocation;
- (void) stopUpdatingLocation;
@end

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreferenceSpecifiers</key>
<array>
<dict>
<key>Title</key>
<string>Privacy Settings</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<!--
<dict>
<key>DefaultValue</key>
<true/>
<key>Key</key>
<string>mapbox_metrics_disabled</string>
<key>Title</key>
<string>Mapbox Metrics Are Disabled</string>
<key>Type</key>
<string>PSTitleValueSpecifier</string>
<key>TrueValue</key>
<true/>
<key>FalseValue</key>
<false/>
</dict>
-->
<dict>
<key>DefaultValue</key>
<true/>
<key>Key</key>
<string>mapbox_metrics_enabled_preference</string>
<key>Title</key>
<string>Mapbox Metrics</string>
<key>Type</key>
<string>PSToggleSwitchSpecifier</string>
<key>TrueValue</key>
<true/>
<key>FalseValue</key>
<false/>
</dict>
</array>
<key>StringsTable</key>
<string>Root</string>
</dict>
</plist>

Binary file not shown.

View File

@@ -23,6 +23,8 @@
#import "NSDictionary+MGLAdditions.h"
#import <algorithm>
#import "MGLMapboxEvents.h"
#import "MGLMetricsLocationManager.h"
// Returns the path to the default cache database on this system.
const std::string &defaultCacheDatabase() {
@@ -179,6 +181,7 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
if (accessToken)
{
mbglMap->setAccessToken((std::string)[accessToken cStringUsingEncoding:[NSString defaultCStringEncoding]]);
[[MGLMapboxEvents sharedManager] setToken:accessToken];
}
}
@@ -217,6 +220,17 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
// setup accessibility
//
self.accessibilityLabel = @"Map";
// setup Metrics
MGLMapboxEvents *events = [MGLMapboxEvents sharedManager];
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
NSString *appVersion = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
if (appName != nil) {
events.appName = appName;
}
if (appVersion != nil) {
events.appVersion = appVersion;
}
// create GL view
//
@@ -357,6 +371,9 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appDidBackground:) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(appWillForeground:) name:UIApplicationWillEnterForegroundNotification object:nil];
// Setup MBLocationManager for metrics
[MGLMetricsLocationManager sharedManager];
// set initial position
//
mbglMap->setLatLngZoom(mbgl::LatLng(0, 0), mbglMap->getMinZoom());
@@ -369,6 +386,28 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
// start the main loop
mbglMap->start();
// Fire map.load on a background thread
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSMutableDictionary *evt = [[NSMutableDictionary alloc] init];
[evt setValue:[[NSNumber alloc] initWithDouble:mbglMap->getLatLng().latitude] forKey:@"lat"];
[evt setValue:[[NSNumber alloc] initWithDouble:mbglMap->getLatLng().longitude] forKey:@"lng"];
[evt setValue:[[NSNumber alloc] initWithDouble:mbglMap->getZoom()] forKey:@"zoom"];
[[MGLMapboxEvents sharedManager] pushEvent:@"map.load" withAttributes:evt];
[evt setValue:[[NSNumber alloc] initWithBool:[[UIApplication sharedApplication] isRegisteredForRemoteNotifications]] forKey:@"enabled.push"];
NSString *email = @"Unknown";
Class MFMailComposeViewController = NSClassFromString(@"MFMailComposeViewController");
if (MFMailComposeViewController) {
SEL canSendMail = NSSelectorFromString(@"canSendMail");
BOOL sendMail = ((BOOL (*)(id, SEL))[MFMailComposeViewController methodForSelector:canSendMail])(MFMailComposeViewController, canSendMail);
email = [NSString stringWithFormat:@"%i", sendMail];
}
[evt setValue:email forKey:@"enabled.email"];
});
return YES;
}
@@ -546,6 +585,9 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)appDidBackground:(NSNotification *)notification
{
// Flush Any Events Still In Queue
[[MGLMapboxEvents sharedManager] flush];
mbglMap->stop();
[self.glView deleteDrawable];
@@ -580,6 +622,8 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)handlePanGesture:(UIPanGestureRecognizer *)pan
{
[self trackGestureEvent:@"Pan" forRecognizer:pan];
if ( ! self.isScrollEnabled) return;
mbglMap->cancelTransitions();
@@ -631,11 +675,23 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
[weakSelf notifyMapChange:@(mbgl::MapChangeRegionDidChangeAnimated)];
}];
}
// Send Map Drag End Event
CGPoint ptInView = CGPointMake([pan locationInView:pan.view].x, [pan locationInView:pan.view].y);
CLLocationCoordinate2D coord = [self convertPoint:ptInView toCoordinateFromView:pan.view];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:[[NSNumber alloc] initWithDouble:coord.latitude] forKey:@"lat"];
[dict setValue:[[NSNumber alloc] initWithDouble:coord.longitude] forKey:@"lng"];
[dict setValue:[[NSNumber alloc] initWithDouble:[self zoomLevel]] forKey:@"zoom"];
[[MGLMapboxEvents sharedManager] pushEvent:@"map.dragend" withAttributes:dict];
}
}
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinch
{
[self trackGestureEvent:@"Pinch" forRecognizer:pinch];
if ( ! self.isZoomEnabled) return;
if (mbglMap->getZoom() <= mbglMap->getMinZoom() && pinch.scale < 1) return;
@@ -670,6 +726,8 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)handleRotateGesture:(UIRotationGestureRecognizer *)rotate
{
[self trackGestureEvent:@"Rotation" forRecognizer:rotate];
if ( ! self.isRotateEnabled) return;
mbglMap->cancelTransitions();
@@ -708,8 +766,10 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTap
{
[self trackGestureEvent:@"SingleTap" forRecognizer:singleTap];
CGPoint tapPoint = [singleTap locationInView:self];
// tolerances based on touch size & typical marker aspect ratio
CGFloat toleranceWidth = 50;
CGFloat toleranceHeight = 75;
@@ -820,6 +880,8 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)handleDoubleTapGesture:(UITapGestureRecognizer *)doubleTap
{
[self trackGestureEvent:@"DoubleTap" forRecognizer:doubleTap];
if ( ! self.isZoomEnabled) return;
mbglMap->cancelTransitions();
@@ -845,6 +907,8 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)handleTwoFingerTapGesture:(UITapGestureRecognizer *)twoFingerTap
{
[self trackGestureEvent:@"TwoFingerTap" forRecognizer:twoFingerTap];
if ( ! self.isZoomEnabled) return;
if (mbglMap->getZoom() == mbglMap->getMinZoom()) return;
@@ -872,6 +936,8 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
- (void)handleQuickZoomGesture:(UILongPressGestureRecognizer *)quickZoom
{
[self trackGestureEvent:@"QuickZoom" forRecognizer:quickZoom];
if ( ! self.isZoomEnabled) return;
mbglMap->cancelTransitions();
@@ -907,6 +973,20 @@ mbgl::DefaultFileSource *mbglFileSource = nullptr;
return ([validSimultaneousGestures containsObject:gestureRecognizer] && [validSimultaneousGestures containsObject:otherGestureRecognizer]);
}
- (void) trackGestureEvent:(NSString *) gesture forRecognizer:(UIGestureRecognizer *) recognizer
{
// Send Map Zoom Event
CGPoint ptInView = CGPointMake([recognizer locationInView:recognizer.view].x, [recognizer locationInView:recognizer.view].y);
CLLocationCoordinate2D coord = [self convertPoint:ptInView toCoordinateFromView:recognizer.view];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:[[NSNumber alloc] initWithDouble:coord.latitude] forKey:@"lat"];
[dict setValue:[[NSNumber alloc] initWithDouble:coord.longitude] forKey:@"lng"];
[dict setValue:[[NSNumber alloc] initWithDouble:[self zoomLevel]] forKey:@"zoom"];
[dict setValue:gesture forKey:@"gesture"];
[[MGLMapboxEvents sharedManager] pushEvent:@"map.click" withAttributes:dict];
}
#pragma mark - Properties -
#pragma clang diagnostic push

View File

@@ -0,0 +1,337 @@
//
// MapboxEvents.m
// MapboxEvents
//
// Dynamic detection of ASIdentifierManager from Mixpanel
// https://github.com/mixpanel/mixpanel-iphone/blob/master/LICENSE
//
// Created by Brad Leege on 3/5/15.
// Copyright (c) 2015 Mapbox. All rights reserved.
//
#import "MGLMapboxEvents.h"
#import <UIKit/UIKit.h>
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
#import <CoreTelephony/CTCarrier.h>
#include <sys/sysctl.h>
#import <SystemConfiguration/CaptiveNetwork.h>
@interface MGLMapboxEvents()
@property (atomic) NSMutableArray *queue;
@property (atomic) NSString *instance;
@property (atomic) NSString *anonid;
@property (atomic) NSTimer *timer;
@property (atomic) NSString *userAgent;
@end
@implementation MGLMapboxEvents
static MGLMapboxEvents *sharedManager = nil;
NSDateFormatter *rfc3339DateFormatter = nil;
NSString *model;
NSString *iOSVersion;
NSString *carrier;
NSNumber *scale;
- (id) init {
self = [super init];
if (self) {
// Put Settings bundle into memory
NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
if(!settingsBundle) {
NSLog(@"Could not find Settings.bundle");
} else {
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:@"Root.plist"]];
NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];
NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
for(NSDictionary *prefSpecification in preferences) {
NSString *key = [prefSpecification objectForKey:@"Key"];
if(key && [[prefSpecification allKeys] containsObject:@"DefaultValue"]) {
[defaultsToRegister setObject:[prefSpecification objectForKey:@"DefaultValue"] forKey:key];
}
}
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];
}
// Configure Events Infrastructure
_queue = [[NSMutableArray alloc] init];
_flushAt = 20;
_flushAfter = 10000;
_api = @"https://api.tiles.mapbox.com";
_token = nil;
_instance = [[NSUUID UUID] UUIDString];
Class ASIdentifierManagerClass = NSClassFromString(@"ASIdentifierManager");
if (ASIdentifierManagerClass) {
SEL sharedManagerSelector = NSSelectorFromString(@"sharedManager");
id sharedManager = ((id (*)(id, SEL))[ASIdentifierManagerClass methodForSelector:sharedManagerSelector])(ASIdentifierManagerClass, sharedManagerSelector);
// Add check here
SEL isAdvertisingTrackingEnabledSelector = NSSelectorFromString(@"isAdvertisingTrackingEnabled");
BOOL trackingEnabled = ((BOOL (*)(id, SEL))[sharedManager methodForSelector:isAdvertisingTrackingEnabledSelector])(sharedManager, isAdvertisingTrackingEnabledSelector);
if (trackingEnabled) {
SEL advertisingIdentifierSelector = NSSelectorFromString(@"advertisingIdentifier");
NSUUID *uuid = ((NSUUID* (*)(id, SEL))[sharedManager methodForSelector:advertisingIdentifierSelector])(sharedManager, advertisingIdentifierSelector);
_anonid = [uuid UUIDString];
} else {
_anonid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
} else {
_anonid = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
model = [self getSysInfoByName:"hw.machine"];
iOSVersion = [NSString stringWithFormat:@"%@ %@", [UIDevice currentDevice].systemName, [UIDevice currentDevice].systemVersion];
if ([UIScreen instancesRespondToSelector:@selector(nativeScale)]) {
scale = [[NSNumber alloc] initWithFloat:[UIScreen mainScreen].nativeScale];
} else {
scale = [[NSNumber alloc] initWithFloat:[UIScreen mainScreen].scale];
}
CTCarrier *carrierVendor = [[[CTTelephonyNetworkInfo alloc] init] subscriberCellularProvider];
carrier = [carrierVendor carrierName];
_userAgent = @"MapboxEventsiOS/1.0";
// Setup Date Format
rfc3339DateFormatter = [[NSDateFormatter alloc] init];
NSLocale *enUSPOSIXLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
[rfc3339DateFormatter setLocale:enUSPOSIXLocale];
[rfc3339DateFormatter setDateFormat:@"yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"];
[rfc3339DateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];
}
return self;
}
+ (id)sharedManager {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[self alloc] init];
});
return sharedManager;
}
- (void) pushEvent:(NSString *)event withAttributes:(NSDictionary *)attributeDictionary {
// Opt Out Checking When Built
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"mapbox_metrics_enabled_preference"]) {
NSLog(@"Mapbox Metrics are not enabled, so clear any currently stored events, and return without sending in data.");
[_queue removeAllObjects];
return;
}
// Add Metrics Disabled App Wide Check
if ([[NSUserDefaults standardUserDefaults] objectForKey:@"mapbox_metrics_disabled"] != nil) {
NSLog(@"Mapbox Metrics have been disabled for this app.");
[_queue removeAllObjects];
return;
}
if (!event) {
return;
}
NSMutableDictionary *evt = [[NSMutableDictionary alloc] init];
// mapbox-events stock attributes
[evt setObject:event forKey:@"event"];
[evt setObject:[NSNumber numberWithInt:1] forKey:@"version"];
[evt setObject:[self formatDate:[NSDate date]] forKey:@"created"];
[evt setObject:self.instance forKey:@"instance"];
[evt setObject:self.anonid forKey:@"anonid"];
// mapbox-events-ios stock attributes
[evt setValue:[rfc3339DateFormatter stringFromDate:[NSDate date]] forKey:@"deviceTimestamp"];
[evt setValue:model forKey:@"model"];
[evt setValue:iOSVersion forKey:@"operatingSystem"];
[evt setValue:[self getDeviceOrientation] forKey:@"orientation"];
[evt setValue:[[NSNumber alloc] initWithFloat:(100 * [UIDevice currentDevice].batteryLevel)] forKey:@"batteryLevel"];
[evt setValue:scale forKey:@"resolution"];
[evt setValue:carrier forKey:@"carrier"];
[evt setValue:[self getWifiNetworkName] forKey:@"wifi"];
[evt setValue:[NSNumber numberWithInt:[self getContentSizeScale]] forKey:@"accessibilityFontScale"];
for (NSString *key in [attributeDictionary allKeys]) {
[evt setObject:[attributeDictionary valueForKey:key] forKey:key];
}
// Make Immutable Version
NSDictionary *finalEvent = [NSDictionary dictionaryWithDictionary:evt];
// Put On The Queue
[self.queue addObject:finalEvent];
// Has Flush Limit Been Reached?
if ((int)_queue.count >= (int)_flushAt) {
[self flush];
}
// Reset Timer (Initial Starting of Timer after first event is pushed)
[self startTimer];
}
- (void) flush {
if (_token == nil) {
NSLog(@"token hasn't been set yet, so no events can be sent. return");
return;
}
int upper = (int)_flushAt;
if (_flushAt > [_queue count]) {
if ([_queue count] == 0) {
return;
}
upper = (int)[_queue count];
}
// Create Array of Events to push to the Server
NSRange theRange = NSMakeRange(0, upper);
NSArray *events = [_queue subarrayWithRange:theRange];
// Update Queue to remove events sent to server
[_queue removeObjectsInRange:theRange];
// Send Array of Events to Server
[self postEvents:events];
}
- (void) postEvents:(NSArray *)events {
// Setup URL Request
NSString *url = [NSString stringWithFormat:@"%@/events/v1?access_token=%@", _api, _token];
NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:url]];
[request setValue:[self getUserAgent] forHTTPHeaderField:@"User-Agent"];
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[request setHTTPMethod:@"POST"];
// Convert Array of Dictionaries to JSON
if ([NSJSONSerialization isValidJSONObject:events]) {
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:events options:NSJSONWritingPrettyPrinted error:nil];
[request setHTTPBody:jsonData];
// Send non blocking HTTP Request to server
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:nil];
}
}
- (void) startTimer {
// Stop Timer if it already exists
if (_timer) {
[_timer invalidate];
_timer = nil;
}
// Start New Timer
NSTimeInterval interval = (double)((NSInteger)_flushAfter);
_timer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(flush) userInfo:nil repeats:YES];
}
- (NSString *) getUserAgent {
if (_appName != nil && _appVersion != nil && ([_userAgent rangeOfString:_appName].location == NSNotFound)) {
_userAgent = [NSString stringWithFormat:@"%@/%@ %@", _appName, _appVersion, _userAgent];
}
return _userAgent;
}
- (NSString *) formatDate:(NSDate *)date {
return [rfc3339DateFormatter stringFromDate:date];
}
- (NSString *) getDeviceOrientation {
switch ([UIDevice currentDevice].orientation) {
case UIDeviceOrientationUnknown:
return @"Unknown";
break;
case UIDeviceOrientationPortrait:
return @"Portrait";
break;
case UIDeviceOrientationPortraitUpsideDown:
return @"PortraitUpsideDown";
break;
case UIDeviceOrientationLandscapeLeft:
return @"LandscapeLeft";
break;
case UIDeviceOrientationLandscapeRight:
return @"LandscapeRight";
break;
case UIDeviceOrientationFaceUp:
return @"FaceUp";
break;
case UIDeviceOrientationFaceDown:
return @"FaceDown";
break;
default:
return @"Default - Unknown";
break;
}
}
- (int) getContentSizeScale {
NSString *sc = [UIApplication sharedApplication].preferredContentSizeCategory;
if ([sc isEqualToString:UIContentSizeCategoryExtraSmall]) {
return -3;
} else if ([sc isEqualToString:UIContentSizeCategorySmall]) {
return -2;
} else if ([sc isEqualToString:UIContentSizeCategoryMedium]) {
return -1;
} else if ([sc isEqualToString:UIContentSizeCategoryLarge]) {
return 0;
} else if ([sc isEqualToString:UIContentSizeCategoryExtraLarge]) {
return 1;
} else if ([sc isEqualToString:UIContentSizeCategoryExtraExtraLarge]) {
return 2;
} else if ([sc isEqualToString:UIContentSizeCategoryExtraExtraExtraLarge]) {
return 3;
} else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityMedium]) {
return -11;
} else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityLarge]) {
return 10;
} else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraLarge]) {
return 11;
} else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraLarge]) {
return 12;
} else if ([sc isEqualToString:UIContentSizeCategoryAccessibilityExtraExtraExtraLarge]) {
return 13;
}
return -9999;
}
- (NSString *)getSysInfoByName:(char *)typeSpecifier
{
size_t size;
sysctlbyname(typeSpecifier, NULL, &size, NULL, 0);
char *answer = malloc(size);
sysctlbyname(typeSpecifier, answer, &size, NULL, 0);
NSString *results = [NSString stringWithCString:answer encoding: NSUTF8StringEncoding];
free(answer);
return results;
}
- (NSString *) getWifiNetworkName {
NSString *ssid = @"";
CFArrayRef interfaces = CNCopySupportedInterfaces();
if (interfaces) {
NSDictionary *info = (__bridge NSDictionary *)CNCopyCurrentNetworkInfo(CFArrayGetValueAtIndex(interfaces, 0));
if (info) {
ssid = info[@"SSID"];
} else {
ssid = @"<<NONE>>";
}
} else {
ssid = @"<<NONE>>";
}
return ssid;
}
@end

View File

@@ -0,0 +1,129 @@
//
// MBLocationManager.m
// Hermes
//
// Dynamic Settings.bundle loading based on:
// http://stackoverflow.com/questions/510216/can-you-make-the-settings-in-settings-bundle-default-even-if-you-dont-open-the
//
// Created by Brad Leege on 3/8/15.
// Copyright (c) 2015 Mapbox. All rights reserved.
//
#import "MGLMetricsLocationManager.h"
#import "CoreLocation/CoreLocation.h"
#import "MGLMapboxEvents.h"
@interface MGLMetricsLocationManager()
@property (atomic) CLLocationManager *locationManager;
@end
@implementation MGLMetricsLocationManager
static MGLMetricsLocationManager *sharedManager = nil;
- (id) init {
if (self = [super init]) {
NSString *settingsBundle = [[NSBundle mainBundle] pathForResource:@"Settings" ofType:@"bundle"];
if(!settingsBundle) {
NSLog(@"Could not find Settings.bundle");
} else {
NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:[settingsBundle stringByAppendingPathComponent:@"Root.plist"]];
NSArray *preferences = [settings objectForKey:@"PreferenceSpecifiers"];
NSMutableDictionary *defaultsToRegister = [[NSMutableDictionary alloc] initWithCapacity:[preferences count]];
for(NSDictionary *prefSpecification in preferences) {
NSString *key = [prefSpecification objectForKey:@"Key"];
if(key && [[prefSpecification allKeys] containsObject:@"DefaultValue"]) {
[defaultsToRegister setObject:[prefSpecification objectForKey:@"DefaultValue"] forKey:key];
}
}
[[NSUserDefaults standardUserDefaults] registerDefaults:defaultsToRegister];
}
_locationManager = [[CLLocationManager alloc] init];
_locationManager.distanceFilter = 2;
_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
_locationManager.pausesLocationUpdatesAutomatically = YES;
[_locationManager setDelegate:self];
}
return self;
}
+ (id)sharedManager {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[self alloc] init];
});
return sharedManager;
}
- (BOOL) isAuthorizedStatusDetermined {
return ([CLLocationManager authorizationStatus] != kCLAuthorizationStatusNotDetermined);
}
- (void) requestAlwaysAuthorization {
if ([self.locationManager respondsToSelector:@selector(requestAlwaysAuthorization)]) {
[self.locationManager requestAlwaysAuthorization];
} else {
// This is iOS 7 or below so Starting Location Updates will trigger authorization request
[self startUpdatingLocation];
}
}
- (void) startUpdatingLocation {
[self.locationManager startUpdatingLocation];
}
- (void) stopUpdatingLocation {
[self.locationManager stopUpdatingLocation];
}
#pragma mark CLLocationManagerDelegate
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations {
CLLocation *loc = (CLLocation *)[locations objectAtIndex:0];
NSLog(@"didUpdateLocations() called with %lu location in array. First Location = %f, %f", (unsigned long)locations.count, loc.coordinate.latitude, loc.coordinate.longitude);
// Iterate through locations to pass all data
for (CLLocation *loc in locations) {
NSMutableDictionary *evt = [[NSMutableDictionary alloc] init];
[evt setValue:[[NSNumber alloc] initWithDouble:loc.coordinate.latitude] forKey:@"lat"];
[evt setValue:[[NSNumber alloc] initWithDouble:loc.coordinate.longitude] forKey:@"lng"];
[[MGLMapboxEvents sharedManager] pushEvent:@"location" withAttributes:evt];
}
}
- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
NSString *newStatus = nil;
switch (status) {
case kCLAuthorizationStatusNotDetermined:
newStatus = @"User Hasn't Determined Yet";
break;
case kCLAuthorizationStatusRestricted:
newStatus = @"Restricted and Can't Be Changed By User";
break;
case kCLAuthorizationStatusDenied:
newStatus = @"User Explcitly Denied";
[[MGLMetricsLocationManager sharedManager] stopUpdatingLocation];
break;
case kCLAuthorizationStatusAuthorized:
newStatus = @"User Has Authorized / Authorized Always";
[[MGLMetricsLocationManager sharedManager] startUpdatingLocation];
break;
// case kCLAuthorizationStatusAuthorizedAlways:
// newStatus = @"Not Determined";
// break;
case kCLAuthorizationStatusAuthorizedWhenInUse:
newStatus = @"User Has Authorized When In Use Only";
[[MGLMetricsLocationManager sharedManager] startUpdatingLocation];
break;
default:
newStatus = @"Unknown";
break;
}
NSLog(@"MGLMetricsLocationManager didChangeAuthorizationStatus() called. New Status = %@", newStatus);
}
@end