[alohalytics] Updated library version (now with iOS support).
This commit is contained in:
parent
3363aba81c
commit
4f3b0f713e
18 changed files with 557 additions and 32 deletions
|
@ -15,7 +15,10 @@ HEADERS += src/alohalytics.h \
|
|||
QMAKE_LFLAGS *= -lz
|
||||
|
||||
macx-* {
|
||||
OBJECTIVE_SOURCES += src/apple/http_client_apple.mm
|
||||
HEADERS += src/alohalytics_objc.h
|
||||
OBJECTIVE_SOURCES += src/apple/http_client_apple.mm \
|
||||
src/apple/alohalytics_objc.mm \
|
||||
|
||||
QMAKE_OBJECTIVE_CFLAGS *= -fobjc-arc
|
||||
QMAKE_LFLAGS *= -framework Foundation
|
||||
}
|
||||
|
|
|
@ -7,6 +7,15 @@
|
|||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
6B4A80811A9C9D9800A02435 /* alohalytics.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6B4A80801A9C9D9800A02435 /* alohalytics.cc */; };
|
||||
6B4A80821A9C9D9800A02435 /* alohalytics.cc in Sources */ = {isa = PBXBuildFile; fileRef = 6B4A80801A9C9D9800A02435 /* alohalytics.cc */; };
|
||||
6BB30D5A1A8E5F8600BAE535 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 6BD1AAFC1A8E5AD8000CB093 /* libz.dylib */; };
|
||||
6BD1AAF31A8E571A000CB093 /* alohalytics_objc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BD1AAF11A8E571A000CB093 /* alohalytics_objc.mm */; };
|
||||
6BD1AAF41A8E571A000CB093 /* alohalytics_objc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BD1AAF11A8E571A000CB093 /* alohalytics_objc.mm */; };
|
||||
6BD1AAF51A8E571A000CB093 /* http_client_apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BD1AAF21A8E571A000CB093 /* http_client_apple.mm */; };
|
||||
6BD1AAF61A8E571A000CB093 /* http_client_apple.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6BD1AAF21A8E571A000CB093 /* http_client_apple.mm */; };
|
||||
6BD1AAFD1A8E5AD8000CB093 /* libz.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 6BD1AAFC1A8E5AD8000CB093 /* libz.dylib */; };
|
||||
6BDB7FDC1AA4884B0036C3CB /* AdSupport.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6BDB7FDB1AA4884B0036C3CB /* AdSupport.framework */; settings = {ATTRIBUTES = (Weak, ); }; };
|
||||
6BDEF0571A3CF2D100054FAC /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BDEF0561A3CF2D100054FAC /* main.m */; };
|
||||
6BDEF05A1A3CF2D100054FAC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BDEF0591A3CF2D100054FAC /* AppDelegate.m */; };
|
||||
6BDEF05D1A3CF2D100054FAC /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 6BDEF05C1A3CF2D100054FAC /* ViewController.m */; };
|
||||
|
@ -27,6 +36,20 @@
|
|||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
6B4A80801A9C9D9800A02435 /* alohalytics.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = alohalytics.cc; path = ../../../src/cpp/alohalytics.cc; sourceTree = "<group>"; };
|
||||
6B4A80831A9C9DB100A02435 /* alohalytics.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = alohalytics.h; path = ../../../src/alohalytics.h; sourceTree = "<group>"; };
|
||||
6B4A80841A9C9DB100A02435 /* event_base.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = event_base.h; path = ../../../src/event_base.h; sourceTree = "<group>"; };
|
||||
6B4A80851A9C9DB100A02435 /* http_client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = http_client.h; path = ../../../src/http_client.h; sourceTree = "<group>"; };
|
||||
6B4A80861A9C9DB100A02435 /* location.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = location.h; path = ../../../src/location.h; sourceTree = "<group>"; };
|
||||
6B4A80871A9C9DB100A02435 /* logger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = logger.h; path = ../../../src/logger.h; sourceTree = "<group>"; };
|
||||
6BD1AAE71A8E53EC000CB093 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = en; path = en.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
6BD1AAF11A8E571A000CB093 /* alohalytics_objc.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = alohalytics_objc.mm; path = ../../../src/apple/alohalytics_objc.mm; sourceTree = "<group>"; };
|
||||
6BD1AAF21A8E571A000CB093 /* http_client_apple.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = http_client_apple.mm; path = ../../../src/apple/http_client_apple.mm; sourceTree = "<group>"; };
|
||||
6BD1AAF71A8E57B1000CB093 /* alohalytics_objc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = alohalytics_objc.h; path = ../../../src/alohalytics_objc.h; sourceTree = "<group>"; };
|
||||
6BD1AAFB1A8E5ABE000CB093 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
|
||||
6BD1AAFC1A8E5AD8000CB093 /* libz.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libz.dylib; path = usr/lib/libz.dylib; sourceTree = SDKROOT; };
|
||||
6BD1AAFE1A8E5AF7000CB093 /* gzip_wrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gzip_wrapper.h; path = ../../../src/gzip_wrapper.h; sourceTree = "<group>"; };
|
||||
6BDB7FDB1AA4884B0036C3CB /* AdSupport.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AdSupport.framework; path = System/Library/Frameworks/AdSupport.framework; sourceTree = SDKROOT; };
|
||||
6BDEF0511A3CF2D100054FAC /* SampleClient.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SampleClient.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6BDEF0551A3CF2D100054FAC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
6BDEF0561A3CF2D100054FAC /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||
|
@ -34,9 +57,7 @@
|
|||
6BDEF0591A3CF2D100054FAC /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
|
||||
6BDEF05B1A3CF2D100054FAC /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
|
||||
6BDEF05C1A3CF2D100054FAC /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
|
||||
6BDEF05F1A3CF2D100054FAC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
6BDEF0611A3CF2D100054FAC /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
|
||||
6BDEF0641A3CF2D100054FAC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
|
||||
6BDEF06A1A3CF2D100054FAC /* SampleClientTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SampleClientTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6BDEF06F1A3CF2D100054FAC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
6BDEF0701A3CF2D100054FAC /* SampleClientTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SampleClientTests.m; sourceTree = "<group>"; };
|
||||
|
@ -47,6 +68,8 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
6BDB7FDC1AA4884B0036C3CB /* AdSupport.framework in Frameworks */,
|
||||
6BD1AAFD1A8E5AD8000CB093 /* libz.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -54,12 +77,30 @@
|
|||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
6BB30D5A1A8E5F8600BAE535 /* libz.dylib in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
6BD1AAF81A8E57B8000CB093 /* Alohalytics */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6B4A80831A9C9DB100A02435 /* alohalytics.h */,
|
||||
6B4A80841A9C9DB100A02435 /* event_base.h */,
|
||||
6B4A80851A9C9DB100A02435 /* http_client.h */,
|
||||
6B4A80861A9C9DB100A02435 /* location.h */,
|
||||
6B4A80871A9C9DB100A02435 /* logger.h */,
|
||||
6B4A80801A9C9D9800A02435 /* alohalytics.cc */,
|
||||
6BD1AAFE1A8E5AF7000CB093 /* gzip_wrapper.h */,
|
||||
6BD1AAF71A8E57B1000CB093 /* alohalytics_objc.h */,
|
||||
6BD1AAF11A8E571A000CB093 /* alohalytics_objc.mm */,
|
||||
6BD1AAF21A8E571A000CB093 /* http_client_apple.mm */,
|
||||
);
|
||||
name = Alohalytics;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
6BDEF0481A3CF2D100054FAC = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -81,6 +122,7 @@
|
|||
6BDEF0531A3CF2D100054FAC /* SampleClient */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6BD1AAF81A8E57B8000CB093 /* Alohalytics */,
|
||||
6BDEF0581A3CF2D100054FAC /* AppDelegate.h */,
|
||||
6BDEF0591A3CF2D100054FAC /* AppDelegate.m */,
|
||||
6BDEF05B1A3CF2D100054FAC /* ViewController.h */,
|
||||
|
@ -96,6 +138,8 @@
|
|||
6BDEF0541A3CF2D100054FAC /* Supporting Files */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6BDB7FDB1AA4884B0036C3CB /* AdSupport.framework */,
|
||||
6BD1AAFC1A8E5AD8000CB093 /* libz.dylib */,
|
||||
6BDEF0551A3CF2D100054FAC /* Info.plist */,
|
||||
6BDEF0561A3CF2D100054FAC /* main.m */,
|
||||
);
|
||||
|
@ -181,7 +225,6 @@
|
|||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 6BDEF0481A3CF2D100054FAC;
|
||||
productRefGroup = 6BDEF0521A3CF2D100054FAC /* Products */;
|
||||
|
@ -220,6 +263,9 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
6BDEF05D1A3CF2D100054FAC /* ViewController.m in Sources */,
|
||||
6BD1AAF31A8E571A000CB093 /* alohalytics_objc.mm in Sources */,
|
||||
6BD1AAF51A8E571A000CB093 /* http_client_apple.mm in Sources */,
|
||||
6B4A80811A9C9D9800A02435 /* alohalytics.cc in Sources */,
|
||||
6BDEF05A1A3CF2D100054FAC /* AppDelegate.m in Sources */,
|
||||
6BDEF0571A3CF2D100054FAC /* main.m in Sources */,
|
||||
);
|
||||
|
@ -230,6 +276,9 @@
|
|||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
6BDEF0711A3CF2D100054FAC /* SampleClientTests.m in Sources */,
|
||||
6BD1AAF41A8E571A000CB093 /* alohalytics_objc.mm in Sources */,
|
||||
6BD1AAF61A8E571A000CB093 /* http_client_apple.mm in Sources */,
|
||||
6B4A80821A9C9D9800A02435 /* alohalytics.cc in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -247,7 +296,7 @@
|
|||
6BDEF05E1A3CF2D100054FAC /* Main.storyboard */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
6BDEF05F1A3CF2D100054FAC /* Base */,
|
||||
6BD1AAE71A8E53EC000CB093 /* en */,
|
||||
);
|
||||
name = Main.storyboard;
|
||||
sourceTree = "<group>";
|
||||
|
@ -255,7 +304,7 @@
|
|||
6BDEF0631A3CF2D100054FAC /* LaunchScreen.xib */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
6BDEF0641A3CF2D100054FAC /* Base */,
|
||||
6BD1AAFB1A8E5ABE000CB093 /* en */,
|
||||
);
|
||||
name = LaunchScreen.xib;
|
||||
sourceTree = "<group>";
|
||||
|
@ -282,6 +331,7 @@
|
|||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
CURRENT_PROJECT_VERSION = 1.0;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
|
@ -324,6 +374,7 @@
|
|||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
CURRENT_PROJECT_VERSION = 1.0;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*******************************************************************************
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Alexander Zolotarev <me@alex.bio> from Minsk, Belarus
|
||||
Copyright (c) 2015 Alexander Zolotarev <me@alex.bio> from Minsk, Belarus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -24,6 +24,8 @@
|
|||
|
||||
#import "AppDelegate.h"
|
||||
|
||||
#import "../../../src/alohalytics_objc.h"
|
||||
|
||||
@interface AppDelegate ()
|
||||
|
||||
@end
|
||||
|
@ -33,29 +35,51 @@
|
|||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
||||
// Override point for customization after application launch.
|
||||
[Alohalytics setDebugMode:YES];
|
||||
[Alohalytics setup:@"http://localhost:8080/" withLaunchOptions:launchOptions];
|
||||
|
||||
UIDevice * device = [UIDevice currentDevice];
|
||||
[Alohalytics logEvent:@"deviceInfo" withKeyValueArray:@[@"deviceName", device.name, @"systemName", device.systemName, @"systemVersion", device.systemVersion]];
|
||||
|
||||
// Used for example purposes only to upload statistics in background.
|
||||
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)applicationWillResignActive:(UIApplication *)application {
|
||||
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
|
||||
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
|
||||
[Alohalytics logEvent:@"$willResignActive"];
|
||||
}
|
||||
|
||||
- (void)applicationDidEnterBackground:(UIApplication *)application {
|
||||
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
|
||||
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
|
||||
[Alohalytics logEvent:@"$didEnterBackground"];
|
||||
}
|
||||
|
||||
- (void)applicationWillEnterForeground:(UIApplication *)application {
|
||||
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
|
||||
[Alohalytics logEvent:@"$willEnterForeground"];
|
||||
}
|
||||
|
||||
- (void)applicationDidBecomeActive:(UIApplication *)application {
|
||||
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
|
||||
[Alohalytics logEvent:@"$didBecomeActive"];
|
||||
}
|
||||
|
||||
- (void)applicationWillTerminate:(UIApplication *)application {
|
||||
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
|
||||
[Alohalytics logEvent:@"$willTerminate"];
|
||||
}
|
||||
|
||||
// You have to set "fetch" in UIBackgroundsModes in your plist so system will call this method.
|
||||
// See https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html for more details.
|
||||
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
|
||||
{
|
||||
[Alohalytics forceUpload];
|
||||
completionHandler(UIBackgroundFetchResultNewData);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -15,11 +15,15 @@
|
|||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<string>${CURRENT_PROJECT_VERSION}</string>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
</array>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
*******************************************************************************/
|
||||
|
||||
#import "ViewController.h"
|
||||
#import "alohalytics_objc.h"
|
||||
|
||||
@interface ViewController ()
|
||||
|
||||
|
@ -32,12 +33,15 @@
|
|||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
// Do any additional setup after loading the view, typically from a nib.
|
||||
const CLLocationCoordinate2D coord = {12.12345678, -32.12345678};
|
||||
CLLocation * l = [[CLLocation alloc] initWithCoordinate:coord altitude:42.42424242 horizontalAccuracy:2.123456
|
||||
verticalAccuracy:12.123456 course:90.12345678 speed:1.2345678 timestamp:[NSDate date]];
|
||||
[Alohalytics logEvent:@"ViewControllerDidLoad" withDictionary:[NSBundle mainBundle].infoDictionary atLocation:l];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
[Alohalytics logEvent:@"didReceiveMemoryWarning"];
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
|
@ -69,7 +69,8 @@ inline std::string ReadFileAsString(std::string const& file_name) {
|
|||
const std::streampos size = fi.tellg();
|
||||
std::string buffer(static_cast<const size_t>(size), '\0');
|
||||
fi.seekg(0);
|
||||
if (fi.read(&buffer[0], size).good()) {
|
||||
// TODO(AlexZ): Check if file size is bigger than std::streamsize.
|
||||
if (fi.read(&buffer[0], static_cast<std::streamsize>(size)).good()) {
|
||||
return buffer;
|
||||
} else {
|
||||
// TODO(dkorolev): Ask Alex whether there's a better way than what I have here with two exceptions.
|
||||
|
|
|
@ -200,6 +200,10 @@ class FSQ final : public CONFIG::T_FILE_NAMING_STRATEGY,
|
|||
// Example: App just got updated, or a large external download has just been successfully completed.
|
||||
//
|
||||
// Use `ResumeProcessing()` in other cases.
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
||||
// TODO(AlexZ): Refactor it as this method fails sometimes when force_finalize_current_file == true and when it is called from the main app thread.
|
||||
// As a result, after calling PushMessage(urgentEvent); ForceProcessing(true); urgentEvent is either lost or not sent to the server even if connection was available at that moment.
|
||||
// Some facts: current_file_name_ is empty and status_.appended_file_timestamp is 0 at that time.
|
||||
void ForceProcessing(bool force_finalize_current_file = false) {
|
||||
std::unique_lock<std::mutex> lock(status_mutex_);
|
||||
if (force_finalize_current_file || status_.finalized.queue.empty()) {
|
||||
|
|
53
3party/Alohalytics/src/alohalytics_objc.h
Normal file
53
3party/Alohalytics/src/alohalytics_objc.h
Normal file
|
@ -0,0 +1,53 @@
|
|||
/*******************************************************************************
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Alexander Zolotarev <me@alex.bio> from Minsk, Belarus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*******************************************************************************/
|
||||
|
||||
// Convenience header to use from pure Objective-C project.
|
||||
|
||||
#ifndef ALOHALYTICS_OBJC_H
|
||||
#define ALOHALYTICS_OBJC_H
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <CoreLocation/CoreLocation.h>
|
||||
|
||||
@interface Alohalytics : NSObject
|
||||
+ (void)setDebugMode:(BOOL)enable;
|
||||
// Should be called in application:didFinishLaunchingWithOptions:
|
||||
// or in application:willFinishLaunchingWithOptions:
|
||||
+ (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;
|
||||
+ (void)logEvent:(NSString *)event withValue:(NSString *)value;
|
||||
+ (void)logEvent:(NSString *)event withValue:(NSString *)value atLocation:(CLLocation *)location;
|
||||
// Two convenience methods to log [key1,value1,key2,value2,...] arrays.
|
||||
+ (void)logEvent:(NSString *)event withKeyValueArray:(NSArray *)array;
|
||||
+ (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;
|
||||
@end
|
||||
|
||||
#endif // #ifndef ALOHALYTICS_OBJC_H
|
|
@ -82,15 +82,13 @@ public class Statistics {
|
|||
// Is it a real new install?
|
||||
if (id.second && installTime == updateTime) {
|
||||
logEvent("$install", new String[]{"version", versionName,
|
||||
"secondsBeforeLaunch", String.valueOf((System.currentTimeMillis() - installTime) / 1000)},
|
||||
lastKnownLocation);
|
||||
"millisEpochInstalled", String.valueOf(installTime)}, lastKnownLocation);
|
||||
// Collect device info once on start.
|
||||
SystemInfo.getDeviceInfoAsync(context);
|
||||
prefs.edit().putLong(PREF_APP_UPDATE_TIME, updateTime).apply();
|
||||
} else if (updateTime != installTime && updateTime != prefs.getLong(PREF_APP_UPDATE_TIME, 0)) {
|
||||
logEvent("$update", new String[]{"version", versionName,
|
||||
"secondsBeforeLaunch", String.valueOf((System.currentTimeMillis() - updateTime) / 1000),
|
||||
"userAgeInSeconds", String.valueOf((System.currentTimeMillis() - installTime) / 1000)},
|
||||
"millisEpochUpdated", String.valueOf(updateTime), "millisEpochInstalled", String.valueOf(installTime)},
|
||||
lastKnownLocation);
|
||||
// Also collect device info on update.
|
||||
SystemInfo.getDeviceInfoAsync(context);
|
||||
|
|
|
@ -62,6 +62,8 @@ public class SystemInfo {
|
|||
public void run() {
|
||||
collectIds(context);
|
||||
collectDeviceDetails(context);
|
||||
// Force statistics uploading as if user immediately uninstalls the app we won't even know about installation.
|
||||
Statistics.forceUpload();
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
@ -128,8 +130,6 @@ public class SystemInfo {
|
|||
}
|
||||
|
||||
Statistics.logEvent("$androidIds", ids.mPairs);
|
||||
// Force statistics uploading as if user immediately uninstalls the app we won't even know about installation.
|
||||
Statistics.forceUpload();
|
||||
}
|
||||
|
||||
private static void collectDeviceDetails(Context context) {
|
||||
|
|
|
@ -194,8 +194,8 @@ JNIEXPORT void JNICALL Java_org_alohalytics_Statistics_setupCPP(JNIEnv* env,
|
|||
// Initialize statistics engine at the end of setup function, as it can use globals above.
|
||||
Stats::Instance()
|
||||
.SetClientId(ToStdString(env, installationId))
|
||||
.SetStoragePath(ToStdString(env, storagePath))
|
||||
.SetServerUrl(ToStdString(env, serverUrl));
|
||||
.SetServerUrl(ToStdString(env, serverUrl))
|
||||
.SetStoragePath(ToStdString(env, storagePath));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_org_alohalytics_Statistics_debugCPP(JNIEnv* env, jclass, jboolean enableDebug) {
|
||||
|
|
369
3party/Alohalytics/src/apple/alohalytics_objc.mm
Normal file
369
3party/Alohalytics/src/apple/alohalytics_objc.mm
Normal file
|
@ -0,0 +1,369 @@
|
|||
/*******************************************************************************
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Alexander Zolotarev <me@alex.bio> from Minsk, Belarus
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*******************************************************************************/
|
||||
|
||||
#if ! __has_feature(objc_arc)
|
||||
#error This file must be compiled with ARC. Either turn on ARC for the project or use -fobjc-arc flag
|
||||
#endif
|
||||
|
||||
#import "../alohalytics_objc.h"
|
||||
#include "../alohalytics.h"
|
||||
#include "../logger.h"
|
||||
|
||||
#include <utility> // std::pair
|
||||
#include <sys/xattr.h>
|
||||
#include <TargetConditionals.h> // TARGET_OS_IPHONE
|
||||
|
||||
#import <CoreFoundation/CoreFoundation.h>
|
||||
#import <CoreFoundation/CFURL.h>
|
||||
#import <Foundation/NSURL.h>
|
||||
#if (TARGET_OS_IPHONE > 0) // Works for all iOS devices, including iPad.
|
||||
#import <UIKit/UIDevice.h>
|
||||
#import <UIKit/UIScreen.h>
|
||||
#import <UIKit/UIApplication.h>
|
||||
#import <AdSupport/ASIdentifierManager.h>
|
||||
#endif // TARGET_OS_IPHONE
|
||||
|
||||
using namespace alohalytics;
|
||||
|
||||
namespace {
|
||||
// Conversion from [possible nil] NSString to std::string.
|
||||
static std::string ToStdString(NSString * nsString) {
|
||||
if (nsString) {
|
||||
return std::string([nsString UTF8String]);
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
// Additional check if object can be represented as a string.
|
||||
static std::string ToStdStringSafe(id object) {
|
||||
if ([object isKindOfClass:[NSString class]]) {
|
||||
return ToStdString(object);
|
||||
} else if ([object isKindOfClass:[NSObject class]]) {
|
||||
return ToStdString(((NSObject *)object).description);
|
||||
}
|
||||
return "ERROR: Trying to log neither NSString nor NSObject-inherited object.";
|
||||
}
|
||||
|
||||
// Safe conversion from [possible nil] NSDictionary.
|
||||
static TStringMap ToStringMap(NSDictionary * nsDictionary) {
|
||||
TStringMap map;
|
||||
for (NSString * key in nsDictionary) {
|
||||
map[ToStdString(key)] = ToStdStringSafe([nsDictionary objectForKey:key]);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// Safe conversion from [possible nil] NSArray.
|
||||
static TStringMap ToStringMap(NSArray * nsArray) {
|
||||
TStringMap map;
|
||||
std::string key;
|
||||
for (id item in nsArray) {
|
||||
if (key.empty()) {
|
||||
key = ToStdStringSafe(item);
|
||||
map[key] = "";
|
||||
} else {
|
||||
map[key] = ToStdStringSafe(item);
|
||||
key.clear();
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
// Safe extraction from [possible nil] CLLocation to alohalytics::Location.
|
||||
static Location ExtractLocation(CLLocation * l) {
|
||||
Location extracted;
|
||||
if (!l) {
|
||||
return extracted;
|
||||
}
|
||||
// Validity of values is checked according to Apple's documentation:
|
||||
// https://developer.apple.com/library/ios/documentation/CoreLocation/Reference/CLLocation_Class/
|
||||
if (l.horizontalAccuracy >= 0) {
|
||||
extracted.SetLatLon([l.timestamp timeIntervalSince1970] * 1000.,
|
||||
l.coordinate.latitude, l.coordinate.longitude,
|
||||
l.horizontalAccuracy);
|
||||
}
|
||||
if (l.verticalAccuracy >= 0) {
|
||||
extracted.SetAltitude(l.altitude, l.verticalAccuracy);
|
||||
}
|
||||
if (l.speed >= 0) {
|
||||
extracted.SetSpeed(l.speed);
|
||||
}
|
||||
if (l.course >= 0) {
|
||||
extracted.SetBearing(l.course);
|
||||
}
|
||||
// We don't know location source on iOS.
|
||||
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];
|
||||
return std::to_string(static_cast<uint64_t>([date timeIntervalSince1970] * 1000.));
|
||||
}
|
||||
return std::string("0");
|
||||
}
|
||||
|
||||
#if (TARGET_OS_IPHONE > 0)
|
||||
static std::string RectToString(CGRect const & rect) {
|
||||
return std::to_string(static_cast<int>(rect.origin.x)) + " " + std::to_string(static_cast<int>(rect.origin.y)) + " "
|
||||
+ std::to_string(static_cast<int>(rect.size.width)) + " " + std::to_string(static_cast<int>(rect.size.height));
|
||||
}
|
||||
|
||||
// Logs some basic device's info.
|
||||
static void LogSystemInformation() {
|
||||
UIDevice * device = [UIDevice currentDevice];
|
||||
UIScreen * screen = [UIScreen mainScreen];
|
||||
std::string preferredLanguages;
|
||||
for (NSString * lang in [NSLocale preferredLanguages]) {
|
||||
preferredLanguages += [lang UTF8String] + std::string(" ");
|
||||
}
|
||||
std::string preferredLocalizations;
|
||||
for (NSString * loc in [[NSBundle mainBundle] preferredLocalizations]) {
|
||||
preferredLocalizations += [loc UTF8String] + std::string(" ");
|
||||
}
|
||||
NSLocale * locale = [NSLocale currentLocale];
|
||||
std::string userInterfaceIdiom = "phone";
|
||||
if (device.userInterfaceIdiom == UIUserInterfaceIdiomPad) {
|
||||
userInterfaceIdiom = "pad";
|
||||
} else if (device.userInterfaceIdiom == UIUserInterfaceIdiomUnspecified) {
|
||||
userInterfaceIdiom = "unspecified";
|
||||
}
|
||||
alohalytics::TStringMap info = {{"deviceName", ToStdString(device.name)},
|
||||
{"deviceSystemName", ToStdString(device.systemName)},
|
||||
{"deviceSystemVersion", ToStdString(device.systemVersion)},
|
||||
{"deviceModel", ToStdString(device.model)},
|
||||
{"deviceUserInterfaceIdiom", userInterfaceIdiom},
|
||||
{"screens", std::to_string([UIScreen screens].count)},
|
||||
{"screenBounds", RectToString(screen.bounds)},
|
||||
{"screenScale", std::to_string(screen.scale)},
|
||||
{"preferredLanguages", preferredLanguages},
|
||||
{"preferredLocalizations", preferredLocalizations},
|
||||
{"localeIdentifier", ToStdString([locale objectForKey:NSLocaleIdentifier])},
|
||||
{"calendarIdentifier", ToStdString([[locale objectForKey:NSLocaleCalendar] calendarIdentifier])},
|
||||
{"localeMeasurementSystem", ToStdString([locale objectForKey:NSLocaleMeasurementSystem])},
|
||||
{"localeDecimalSeparator", ToStdString([locale objectForKey:NSLocaleDecimalSeparator])},
|
||||
};
|
||||
if (device.systemVersion.floatValue >= 8.0) {
|
||||
info.emplace("screenNativeBounds", RectToString(screen.nativeBounds));
|
||||
info.emplace("screenNativeScale", std::to_string(screen.nativeScale));
|
||||
}
|
||||
Stats & instance = Stats::Instance();
|
||||
instance.LogEvent("$iosDeviceInfo", info);
|
||||
|
||||
info.clear();
|
||||
if (device.systemVersion.floatValue >= 6.0) {
|
||||
if (device.identifierForVendor) {
|
||||
info.emplace("identifierForVendor", ToStdString(device.identifierForVendor.UUIDString));
|
||||
}
|
||||
if (NSClassFromString(@"ASIdentifierManager")) {
|
||||
ASIdentifierManager * manager = [ASIdentifierManager sharedManager];
|
||||
info.emplace("isAdvertisingTrackingEnabled", manager.isAdvertisingTrackingEnabled ? "YES" : "NO");
|
||||
if (manager.advertisingIdentifier) {
|
||||
info.emplace("advertisingIdentifier", ToStdString(manager.advertisingIdentifier.UUIDString));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!info.empty()) {
|
||||
instance.LogEvent("$iosDeviceIds", info);
|
||||
}
|
||||
}
|
||||
#endif // TARGET_OS_IPHONE
|
||||
|
||||
// Returns <unique id, true if it's the very-first app launch>.
|
||||
static std::pair<std::string, bool> InstallationId() {
|
||||
bool firstLaunch = false;
|
||||
NSUserDefaults * userDataBase = [NSUserDefaults standardUserDefaults];
|
||||
NSString * installationId = [userDataBase objectForKey:@"AlohalyticsInstallationId"];
|
||||
if (installationId == nil) {
|
||||
CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
|
||||
// All iOS IDs start with I:
|
||||
installationId = [@"I:" stringByAppendingString:(NSString *)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, uuid))];
|
||||
CFRelease(uuid);
|
||||
[userDataBase setValue:installationId forKey:@"AlohalyticsInstallationId"];
|
||||
[userDataBase synchronize];
|
||||
firstLaunch = true;
|
||||
}
|
||||
return std::make_pair([installationId UTF8String], firstLaunch);
|
||||
}
|
||||
|
||||
// Returns path to store statistics files.
|
||||
static std::string StoragePath() {
|
||||
// Store files in special directory which is not backed up automatically.
|
||||
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
|
||||
NSString * directory = [[paths firstObject] stringByAppendingString:@"/Alohalytics/"];
|
||||
NSFileManager * fm = [NSFileManager defaultManager];
|
||||
if (![fm fileExistsAtPath:directory]) {
|
||||
if (![fm createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:nil]) {
|
||||
// TODO(AlexZ): Probably we need to log this case to the server in the future.
|
||||
NSLog(@"Alohalytics ERROR: Can't create directory %@.", directory);
|
||||
}
|
||||
#if (TARGET_OS_IPHONE > 0)
|
||||
// Disable iCloud backup for storage folder: https://developer.apple.com/library/iOS/qa/qa1719/_index.html
|
||||
const std::string storagePath = [directory UTF8String];
|
||||
if (NSFoundationVersionNumber >= NSFoundationVersionNumber_iOS_5_1) {
|
||||
CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
|
||||
reinterpret_cast<unsigned char const *>(storagePath.c_str()),
|
||||
storagePath.size(),
|
||||
0);
|
||||
CFErrorRef err;
|
||||
signed char valueOfCFBooleanYes = 1;
|
||||
CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberCharType, &valueOfCFBooleanYes);
|
||||
if (!CFURLSetResourcePropertyForKey(url, kCFURLIsExcludedFromBackupKey, value, &err)) {
|
||||
NSLog(@"Alohalytics ERROR while disabling iCloud backup for directory %@", directory);
|
||||
}
|
||||
CFRelease(value);
|
||||
CFRelease(url);
|
||||
} else {
|
||||
static char const * attrName = "com.apple.MobileBackup";
|
||||
u_int8_t valueOfBooleanYes = 1;
|
||||
const int result = ::setxattr(storagePath.c_str(), attrName, &valueOfBooleanYes, sizeof(valueOfBooleanYes), 0, 0);
|
||||
if (result != 0) {
|
||||
NSLog(@"Alohalytics ERROR while disabling iCloud backup for directory %@", directory);
|
||||
}
|
||||
}
|
||||
#endif // TARGET_OS_IPHONE
|
||||
}
|
||||
if (directory) {
|
||||
return [directory UTF8String];
|
||||
}
|
||||
return std::string("Alohalytics ERROR: Can't retrieve valid storage path.");
|
||||
}
|
||||
|
||||
#if (TARGET_OS_IPHONE > 0)
|
||||
static alohalytics::TStringMap ParseLaunchOptions(NSDictionary * options) {
|
||||
TStringMap parsed;
|
||||
|
||||
NSURL * url = [options objectForKey:UIApplicationLaunchOptionsURLKey];
|
||||
if (url) {
|
||||
parsed.emplace("UIApplicationLaunchOptionsURLKey", ToStdString([url absoluteString]));
|
||||
}
|
||||
NSString * source = [options objectForKey:UIApplicationLaunchOptionsSourceApplicationKey];
|
||||
if (source) {
|
||||
parsed.emplace("UIApplicationLaunchOptionsSourceApplicationKey", ToStdString(source));
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
#endif // TARGET_OS_IPHONE
|
||||
} // namespace
|
||||
|
||||
@implementation Alohalytics
|
||||
|
||||
+ (void)setDebugMode:(BOOL)enable {
|
||||
Stats::Instance().SetDebugMode(enable);
|
||||
}
|
||||
|
||||
+ (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 auto installationId = InstallationId();
|
||||
Stats & instance = Stats::Instance();
|
||||
instance.SetClientId(installationId.first)
|
||||
.SetServerUrl([serverUrl UTF8String])
|
||||
.SetStoragePath(StoragePath());
|
||||
|
||||
// Calculate some basic statistics about installations/updates/launches.
|
||||
NSUserDefaults * userDataBase = [NSUserDefaults standardUserDefaults];
|
||||
NSString * installedVersion = [userDataBase objectForKey:@"AlohalyticsInstalledVersion"];
|
||||
bool forceUpload = false;
|
||||
if (installationId.second && isFirstLaunch && installedVersion == nil) {
|
||||
NSString * version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
|
||||
// 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([[NSBundle mainBundle] executablePath])}});
|
||||
[userDataBase setValue:version forKey:@"AlohalyticsInstalledVersion"];
|
||||
[userDataBase synchronize];
|
||||
#if (TARGET_OS_IPHONE > 0)
|
||||
LogSystemInformation();
|
||||
#else
|
||||
static_cast<void>(options); // Unused variable warning fix.
|
||||
#endif // TARGET_OS_IPHONE
|
||||
forceUpload = true;
|
||||
} else {
|
||||
NSString * version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
|
||||
if (installedVersion == nil || ![installedVersion isEqualToString:version]) {
|
||||
instance.LogEvent("$update", {{"CFBundleShortVersionString", [version UTF8String]},
|
||||
{"documentsTimestampMillis", PathTimestampMillis([NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject])},
|
||||
{"bundleTimestampMillis", PathTimestampMillis([[NSBundle mainBundle] executablePath])}});
|
||||
[userDataBase setValue:version forKey:@"AlohalyticsInstalledVersion"];
|
||||
[userDataBase synchronize];
|
||||
#if (TARGET_OS_IPHONE > 0)
|
||||
LogSystemInformation();
|
||||
#endif // TARGET_OS_IPHONE
|
||||
forceUpload = true;
|
||||
}
|
||||
}
|
||||
instance.LogEvent("$launch"
|
||||
#if (TARGET_OS_IPHONE > 0)
|
||||
, ParseLaunchOptions(options)
|
||||
#endif // TARGET_OS_IPHONE
|
||||
);
|
||||
// Force uploading to get first-time install information before uninstall.
|
||||
if (forceUpload) {
|
||||
instance.Upload();
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)forceUpload {
|
||||
Stats::Instance().Upload();
|
||||
}
|
||||
|
||||
+ (void)logEvent:(NSString *)event {
|
||||
Stats::Instance().LogEvent(ToStdString(event));
|
||||
}
|
||||
|
||||
+ (void)logEvent:(NSString *)event atLocation:(CLLocation *)location {
|
||||
Stats::Instance().LogEvent(ToStdString(event), ExtractLocation(location));
|
||||
}
|
||||
|
||||
+ (void)logEvent:(NSString *)event withValue:(NSString *)value {
|
||||
Stats::Instance().LogEvent(ToStdString(event), ToStdString(value));
|
||||
}
|
||||
|
||||
+ (void)logEvent:(NSString *)event withValue:(NSString *)value atLocation:(CLLocation *)location {
|
||||
Stats::Instance().LogEvent(ToStdString(event), ToStdString(value), ExtractLocation(location));
|
||||
}
|
||||
|
||||
+ (void)logEvent:(NSString *)event withKeyValueArray:(NSArray *)array {
|
||||
Stats::Instance().LogEvent(ToStdString(event), ToStringMap(array));
|
||||
}
|
||||
|
||||
+ (void)logEvent:(NSString *)event withKeyValueArray:(NSArray *)array atLocation:(CLLocation *)location {
|
||||
Stats::Instance().LogEvent(ToStdString(event), ToStringMap(array), ExtractLocation(location));
|
||||
}
|
||||
|
||||
+ (void)logEvent:(NSString *)event withDictionary:(NSDictionary *)dictionary {
|
||||
Stats::Instance().LogEvent(ToStdString(event), ToStringMap(dictionary));
|
||||
}
|
||||
|
||||
+ (void)logEvent:(NSString *)event withDictionary:(NSDictionary *)dictionary atLocation:(CLLocation *)location {
|
||||
Stats::Instance().LogEvent(ToStdString(event), ToStringMap(dictionary), ExtractLocation(location));
|
||||
}
|
||||
|
||||
@end
|
|
@ -67,7 +67,7 @@ bool HTTPClientPlatformWrapper::RunHTTPRequest() {
|
|||
NSString * path = [NSString stringWithUTF8String:post_file_.c_str()];
|
||||
const unsigned long long file_size = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&err].fileSize;
|
||||
if (err) {
|
||||
error_code_ = err.code;
|
||||
error_code_ = static_cast<int>(err.code);
|
||||
if (debug_mode_) {
|
||||
ALOG("ERROR", error_code_, [err.localizedDescription UTF8String]);
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ bool HTTPClientPlatformWrapper::RunHTTPRequest() {
|
|||
NSData * url_data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&err];
|
||||
|
||||
if (response) {
|
||||
error_code_ = response.statusCode;
|
||||
error_code_ = static_cast<int>(response.statusCode);
|
||||
url_received_ = [response.URL.absoluteString UTF8String];
|
||||
if (url_data) {
|
||||
if (received_file_.empty()) {
|
||||
|
@ -96,7 +96,7 @@ bool HTTPClientPlatformWrapper::RunHTTPRequest() {
|
|||
return true;
|
||||
}
|
||||
// Request has failed if we are here.
|
||||
error_code_ = err.code;
|
||||
error_code_ = static_cast<int>(err.code);
|
||||
if (debug_mode_) {
|
||||
ALOG("ERROR", error_code_, ':', [err.localizedDescription UTF8String], "while connecting to", url_requested_);
|
||||
}
|
||||
|
|
|
@ -108,12 +108,13 @@ bool Stats::OnFileReady(const std::string& full_path_to_file) {
|
|||
|
||||
// Append unique installation id in the beginning of each file sent to the server.
|
||||
// It can be empty so all stats data will become anonymous.
|
||||
// TODO(AlexZ): Refactor fsq to silently add it in the beginning of each file.
|
||||
// TODO(AlexZ): Refactor fsq to silently add it in the beginning of each file
|
||||
// and to avoid not-enough-memory situation for bigger files.
|
||||
std::ifstream fi(full_path_to_file, std::ifstream::in | std::ifstream::binary);
|
||||
std::string buffer(unique_client_id_event_);
|
||||
const size_t id_size = unique_client_id_event_.size();
|
||||
buffer.resize(id_size + file_size);
|
||||
fi.read(&buffer[id_size], file_size);
|
||||
buffer.resize(id_size + static_cast<std::string::size_type>(file_size));
|
||||
fi.read(&buffer[id_size], static_cast<std::streamsize>(file_size));
|
||||
if (fi.good()) {
|
||||
fi.close();
|
||||
return UploadBuffer(upload_url_, std::move(buffer), debug_mode_);
|
||||
|
@ -253,6 +254,7 @@ void Stats::Upload() {
|
|||
LOG_IF_DEBUG("Warning: unique client ID is not set so statistics was not uploaded.");
|
||||
return;
|
||||
}
|
||||
LOG_IF_DEBUG("Forcing statistics uploading.");
|
||||
if (file_storage_queue_) {
|
||||
file_storage_queue_->ForceProcessing();
|
||||
} else {
|
||||
|
@ -263,6 +265,7 @@ void Stats::Upload() {
|
|||
for (const auto& evt : copy) {
|
||||
buffer.append(evt);
|
||||
}
|
||||
LOG_IF_DEBUG("Forcing in-memory statistics uploading.");
|
||||
if (!UploadBuffer(upload_url_, std::move(buffer), debug_mode_)) {
|
||||
// If failed, merge events we tried to upload with possible new ones.
|
||||
memory_storage_.splice(memory_storage_.end(), std::move(copy));
|
||||
|
|
|
@ -54,13 +54,14 @@ inline std::string Gzip(const std::string& data_to_compress) throw(GzipErrorExce
|
|||
int res = ::deflateInit2(&z, Z_BEST_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY);
|
||||
if (Z_OK == res) {
|
||||
z.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(data_to_compress.data()));
|
||||
z.avail_in = data_to_compress.size();
|
||||
// TODO(AlexZ): Check situation when uInt is < than size of the data to compress.
|
||||
z.avail_in = static_cast<uInt>(data_to_compress.size());
|
||||
std::string compressed;
|
||||
std::vector<Bytef> buffer;
|
||||
buffer.resize(std::min(data_to_compress.size(), kGzipBufferSize));
|
||||
do {
|
||||
z.next_out = buffer.data();
|
||||
z.avail_out = buffer.size();
|
||||
z.avail_out = static_cast<uInt>(buffer.size());
|
||||
res = ::deflate(&z, Z_FINISH);
|
||||
if (compressed.size() < z.total_out) {
|
||||
compressed.append(reinterpret_cast<const char*>(buffer.data()), z.total_out - compressed.size());
|
||||
|
@ -79,13 +80,13 @@ inline std::string Gunzip(const std::string& data_to_decompress) throw(GzipError
|
|||
int res = ::inflateInit2(&z, 16 + MAX_WBITS);
|
||||
if (Z_OK == res) {
|
||||
z.next_in = const_cast<Bytef*>(reinterpret_cast<const Bytef*>(data_to_decompress.data()));
|
||||
z.avail_in = data_to_decompress.size();
|
||||
z.avail_in = static_cast<uInt>(data_to_decompress.size());
|
||||
std::string decompressed;
|
||||
std::vector<Bytef> buffer;
|
||||
buffer.resize(std::min(data_to_decompress.size(), kGzipBufferSize));
|
||||
do {
|
||||
z.next_out = buffer.data();
|
||||
z.avail_out = buffer.size();
|
||||
z.avail_out = static_cast<uInt>(buffer.size());
|
||||
res = ::inflate(&z, Z_NO_FLUSH);
|
||||
if (decompressed.size() < z.total_out) {
|
||||
decompressed.append(reinterpret_cast<char const*>(buffer.data()), z.total_out - decompressed.size());
|
||||
|
|
|
@ -77,7 +77,13 @@ class Logger {
|
|||
}
|
||||
|
||||
// String specialization to avoid printing every character as a container's element.
|
||||
void Log(const std::string& t) { out_ << t; }
|
||||
void Log(const std::string& t) {
|
||||
if (t.empty()) {
|
||||
out_ << "<EMPTY_STRING>";
|
||||
} else {
|
||||
out_ << t;
|
||||
}
|
||||
}
|
||||
|
||||
// Pretty-printing for containers.
|
||||
template <template <typename, typename...> class ContainerType, typename ValueType, typename... Args>
|
||||
|
@ -86,8 +92,12 @@ class Logger {
|
|||
size_t index = 0;
|
||||
const size_t count = c.size();
|
||||
for (const auto& v : c) {
|
||||
out_ << v << (++index == count ? '}' : ',');
|
||||
out_ << v;
|
||||
if (++index != count) {
|
||||
out_ << ',';
|
||||
}
|
||||
}
|
||||
out_ << '}';
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Reference in a new issue