Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mapsme/omim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Zolotarev <alex@maps.me>2015-08-06 21:54:16 +0300
committerAlex Zolotarev <alex@maps.me>2015-09-23 03:00:15 +0300
commit914515733940c16d98412adba4981484fdd567b5 (patch)
tree53f278fe837de9ac58db5a610652cb716d16984d
parent0cd24f656e69356e35354558e39ca9bcda4d7ce3 (diff)
[alohalytics][ios] Measure total time spent in the app, get install/update/build/first launch date.
-rw-r--r--3party/Alohalytics/examples/ios/SampleClient/AppDelegate.m6
-rw-r--r--3party/Alohalytics/src/alohalytics_objc.h20
-rw-r--r--3party/Alohalytics/src/apple/alohalytics_objc.mm115
-rw-r--r--iphone/Maps/Classes/MapsAppDelegate.mm2
4 files changed, 120 insertions, 23 deletions
diff --git a/3party/Alohalytics/examples/ios/SampleClient/AppDelegate.m b/3party/Alohalytics/examples/ios/SampleClient/AppDelegate.m
index 3c702d333b..adffc904d4 100644
--- a/3party/Alohalytics/examples/ios/SampleClient/AppDelegate.m
+++ b/3party/Alohalytics/examples/ios/SampleClient/AppDelegate.m
@@ -44,6 +44,12 @@
// Used for example purposes only to upload statistics (unpredictable) in background, when system wakes app up.
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
+ NSLog(@"isFirstSession: %d", [Alohalytics isFirstSession]);
+ NSLog(@"firstLaunchDate: %@", [Alohalytics firstLaunchDate]);
+ NSLog(@"installDate: %@", [Alohalytics installDate]);
+ NSLog(@"updateDate: %@", [Alohalytics updateDate]);
+ NSLog(@"buildDate: %@", [Alohalytics buildDate]);
+
return YES;
}
diff --git a/3party/Alohalytics/src/alohalytics_objc.h b/3party/Alohalytics/src/alohalytics_objc.h
index 5396471b5b..922c79089c 100644
--- a/3party/Alohalytics/src/alohalytics_objc.h
+++ b/3party/Alohalytics/src/alohalytics_objc.h
@@ -23,11 +23,13 @@
*******************************************************************************/
// Convenience header to use from pure Objective-C project.
+// TODO(AlexZ): Refactor it to use instance and it's methods instead of static functions.
#ifndef ALOHALYTICS_OBJC_H
#define ALOHALYTICS_OBJC_H
#import <Foundation/Foundation.h>
+#import <Foundation/NSDate.h>
#import <CoreLocation/CoreLocation.h>
@interface Alohalytics : NSObject
@@ -35,9 +37,6 @@
// Should be called in application:didFinishLaunchingWithOptions: or in application:willFinishLaunchingWithOptions:
// Final serverUrl is modified to $(serverUrl)/[ios|mac]/your.bundle.id/app.version
+ (void)setup:(NSString *)serverUrl withLaunchOptions:(NSDictionary *)options;
-// Alternative to the previous setup method if you integrated Alohalytics after initial release
-// and don't want to count app upgrades as new installs (and definitely know that it's an already existing user).
-+ (void)setup:(NSString *)serverUrl andFirstLaunch:(BOOL)isFirstLaunch withLaunchOptions:(NSDictionary *)options;
+ (void)forceUpload;
+ (void)logEvent:(NSString *)event;
+ (void)logEvent:(NSString *)event atLocation:(CLLocation *)location;
@@ -48,6 +47,21 @@
+ (void)logEvent:(NSString *)event withKeyValueArray:(NSArray *)array atLocation:(CLLocation *)location;
+ (void)logEvent:(NSString *)event withDictionary:(NSDictionary *)dictionary;
+ (void)logEvent:(NSString *)event withDictionary:(NSDictionary *)dictionary atLocation:(CLLocation *)location;
+// Returns YES if it is a first session, before app goes into background.
++ (BOOL)isFirstSession;
+// Returns summary time of all active user sessions up to now.
++ (NSInteger)totalSecondsSpentInTheApp;
+
+// Returns the date when app was launched for the first time (usually the same as install date).
++ (NSDate *)firstLaunchDate;
+// When app was installed (it's Documents folder creation date).
+// Note: firstLaunchDate is usually later than installDate.
++ (NSDate *)installDate;
+// When app was updated (~== installDate for the first installation, it's Resources folder creation date).
++ (NSDate *)updateDate;
+// When the binary was built.
+// Hint: if buildDate > installDate then this is not a new app install, but an existing old user.
++ (NSDate *)buildDate;
@end
#endif // #ifndef ALOHALYTICS_OBJC_H
diff --git a/3party/Alohalytics/src/apple/alohalytics_objc.mm b/3party/Alohalytics/src/apple/alohalytics_objc.mm
index 6adc88f34a..b9820d7b7b 100644
--- a/3party/Alohalytics/src/apple/alohalytics_objc.mm
+++ b/3party/Alohalytics/src/apple/alohalytics_objc.mm
@@ -123,11 +123,9 @@ static Location ExtractLocation(CLLocation * l) {
return extracted;
}
-// Returns string representing uint64_t timestamp of given file or directory (modification date in millis from 1970).
-static std::string PathTimestampMillis(NSString * path) {
- NSDictionary * attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:nil];
- if (attributes) {
- NSDate * date = [attributes objectForKey:NSFileModificationDate];
+// Internal helper.
+static std::string NSDateToMillisFrom1970(NSDate * date) {
+ if (date) {
return std::to_string(static_cast<uint64_t>([date timeIntervalSince1970] * 1000.));
}
return std::string("0");
@@ -328,6 +326,14 @@ bool IsConnectionActive() {
// Keys for NSUserDefaults.
static NSString * const kInstalledVersionKey = @"AlohalyticsInstalledVersion";
+static NSString * const kFirstLaunchDateKey = @"AlohalyticsFirstLaunchDate";
+static NSString * const kTotalSecondsInTheApp = @"AlohalyticsTotalSecondsInTheApp";
+
+// Used to calculate session length and total time spent in the app.
+// setup should be called to activate counting.
+static NSDate * gSessionStartTime = nil;
+static BOOL gIsFirstSession = NO;
+
@implementation Alohalytics
+ (void)setDebugMode:(BOOL)enable {
@@ -335,10 +341,6 @@ static NSString * const kInstalledVersionKey = @"AlohalyticsInstalledVersion";
}
+ (void)setup:(NSString *)serverUrl withLaunchOptions:(NSDictionary *)options {
- [Alohalytics setup:serverUrl andFirstLaunch:YES withLaunchOptions:options];
-}
-
-+ (void)setup:(NSString *)serverUrl andFirstLaunch:(BOOL)isFirstLaunch withLaunchOptions:(NSDictionary *)options {
const NSBundle * bundle = [NSBundle mainBundle];
NSString * bundleIdentifier = [bundle bundleIdentifier];
NSString * version = [[bundle infoDictionary] objectForKey:@"CFBundleShortVersionString"];
@@ -372,21 +374,26 @@ static NSString * const kInstalledVersionKey = @"AlohalyticsInstalledVersion";
NSUserDefaults * userDataBase = [NSUserDefaults standardUserDefaults];
NSString * installedVersion = [userDataBase objectForKey:kInstalledVersionKey];
BOOL shouldSendUpdatedSystemInformation = NO;
- if (installationId.second && isFirstLaunch && installedVersion == nil) {
- // Documents folder modification time can be interpreted as a "first app launch time" or an approx. "app install time".
- // App bundle modification time can be interpreted as an "app update time".
- instance.LogEvent("$install", {{"CFBundleShortVersionString", [version UTF8String]},
- {"documentsTimestampMillis", PathTimestampMillis([NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject])},
- {"bundleTimestampMillis", PathTimestampMillis([bundle executablePath])}});
+ // Do not generate $install event for old users who did not have Alohalytics installed but already used the app.
+ const BOOL appWasNeverInstalledAndLaunchedBefore = (NSOrderedAscending == [[Alohalytics buildDate] compare:[Alohalytics installDate]]);
+ if (installationId.second && appWasNeverInstalledAndLaunchedBefore && installedVersion == nil) {
+ gIsFirstSession = YES;
+ instance.LogEvent("$install", [Alohalytics bundleInformation:version]);
[userDataBase setValue:version forKey:kInstalledVersionKey];
+ // Also store first launch date for future use.
+ if (nil == [userDataBase objectForKey:kFirstLaunchDateKey]) {
+ [userDataBase setObject:[NSDate date] forKey:kFirstLaunchDateKey];
+ }
[userDataBase synchronize];
shouldSendUpdatedSystemInformation = YES;
} else {
if (installedVersion == nil || ![installedVersion isEqualToString:version]) {
- instance.LogEvent("$update", {{"CFBundleShortVersionString", [version UTF8String]},
- {"documentsTimestampMillis", PathTimestampMillis([NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject])},
- {"bundleTimestampMillis", PathTimestampMillis([bundle executablePath])}});
+ instance.LogEvent("$update", [Alohalytics bundleInformation:version]);
[userDataBase setValue:version forKey:kInstalledVersionKey];
+ // Also store first launch date for future use, if Alohalytics was integrated only in this update.
+ if (nil == [userDataBase objectForKey:kFirstLaunchDateKey]) {
+ [userDataBase setObject:[NSDate date] forKey:kFirstLaunchDateKey];
+ }
[userDataBase synchronize];
shouldSendUpdatedSystemInformation = YES;
}
@@ -409,6 +416,14 @@ static NSString * const kInstalledVersionKey = @"AlohalyticsInstalledVersion";
#endif // TARGET_OS_IPHONE
}
++(alohalytics::TStringMap)bundleInformation:(NSString *)version {
+ return {{"CFBundleShortVersionString", [version UTF8String]},
+ {"installTimestampMillis", NSDateToMillisFrom1970([Alohalytics installDate])},
+ {"updateTimestampMillis", NSDateToMillisFrom1970([Alohalytics updateDate])},
+ {"buildTimestampMillis", NSDateToMillisFrom1970([Alohalytics buildDate])},
+ };
+}
+
+ (void)forceUpload {
Stats::Instance().Upload();
}
@@ -448,11 +463,24 @@ static NSString * const kInstalledVersionKey = @"AlohalyticsInstalledVersion";
#pragma mark App lifecycle notifications used to calculate basic metrics.
#if (TARGET_OS_IPHONE > 0)
+ (void)applicationDidBecomeActive:(NSNotification *)notification {
+ gSessionStartTime = [NSDate date];
Stats::Instance().LogEvent("$applicationDidBecomeActive");
}
+ (void)applicationWillResignActive:(NSNotification *)notification {
- Stats::Instance().LogEvent("$applicationWillResignActive");
+ // Calculate session length.
+ NSInteger seconds = static_cast<NSInteger>(-gSessionStartTime.timeIntervalSinceNow);
+ // nil it to filter time when the app is in the background, but totalSecondsSpentInTheApp is called.
+ gSessionStartTime = nil;
+ Stats & instance = Stats::Instance();
+ instance.LogEvent("$applicationWillResignActive", std::to_string(seconds));
+ NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
+ seconds += [defaults integerForKey:kTotalSecondsInTheApp];
+ [defaults setInteger:seconds forKey:kTotalSecondsInTheApp];
+ [defaults synchronize];
+ if (instance.DebugMode()) {
+ ALOG("Total seconds spent in the app:", seconds);
+ }
}
+ (void)applicationWillEnterForeground:(NSNotificationCenter *)notification {
@@ -479,10 +507,59 @@ static NSString * const kInstalledVersionKey = @"AlohalyticsInstalledVersion";
ALOG("Skipped statistics uploading as connection is not active.");
}
}
+ if (gIsFirstSession) {
+ gIsFirstSession = NO;
+ }
}
+ (void)applicationWillTerminate:(NSNotification *)notification {
Stats::Instance().LogEvent("$applicationWillTerminate");
}
#endif // TARGET_OS_IPHONE
+
+#pragma mark Utility methods
+
++ (BOOL)isFirstSession {
+ return gIsFirstSession;
+}
+
++ (NSDate *)firstLaunchDate {
+ NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
+ NSDate * date = [defaults objectForKey:kFirstLaunchDateKey];
+ if (!date) {
+ // Non-standard situation: this method is called before calling setup. Return current date.
+ date = [NSDate date];
+ [defaults setObject:date forKey:kFirstLaunchDateKey];
+ [defaults synchronize];
+ }
+ return date;
+}
+
++ (NSInteger)totalSecondsSpentInTheApp {
+ NSInteger seconds = [[NSUserDefaults standardUserDefaults] integerForKey:kTotalSecondsInTheApp];
+ // Take into an account currently active session.
+ if (gSessionStartTime) {
+ seconds += static_cast<NSInteger>(-gSessionStartTime.timeIntervalSinceNow);
+ }
+ return seconds;
+}
+
+// Internal helper, returns nil for invalid paths.
++ (NSDate *)fileCreationDate:(NSString *)fullPath {
+ NSDictionary * attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:fullPath error:nil];
+ return attributes ? [attributes objectForKey:NSFileCreationDate] : nil;
+}
+
++ (NSDate *)installDate {
+ return [Alohalytics fileCreationDate:[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]];
+}
+
++ (NSDate *)updateDate {
+ return [Alohalytics fileCreationDate:[[NSBundle mainBundle] resourcePath]];
+}
+
++ (NSDate *)buildDate {
+ return [Alohalytics fileCreationDate:[[NSBundle mainBundle] executablePath]];
+}
+
@end
diff --git a/iphone/Maps/Classes/MapsAppDelegate.mm b/iphone/Maps/Classes/MapsAppDelegate.mm
index 1696125f4e..3c30d56708 100644
--- a/iphone/Maps/Classes/MapsAppDelegate.mm
+++ b/iphone/Maps/Classes/MapsAppDelegate.mm
@@ -167,7 +167,7 @@ void InitLocalizedStrings()
#ifndef OMIM_PRODUCTION
[Alohalytics setDebugMode:YES];
#endif
- [Alohalytics setup:@"http://localhost:8080" andFirstLaunch:[MapsAppDelegate isFirstAppLaunch] withLaunchOptions:launchOptions];
+ [Alohalytics setup:@"http://localhost:8080" withLaunchOptions:launchOptions];
NSURL *url = launchOptions[UIApplicationLaunchOptionsURLKey];
if (url != nil)