forked from organicmaps/organicmaps
[alohalytics] Fresh fixes for statistics.
This commit is contained in:
parent
1d412e8375
commit
71c668a49b
24 changed files with 882 additions and 146 deletions
|
@ -3,16 +3,19 @@ CONFIG *= console c++11
|
|||
CONFIG -= app_bundle qt
|
||||
|
||||
SOURCES += examples/cpp/example.cc \
|
||||
src/alohalytics.cc \
|
||||
src/cpp/alohalytics.cc \
|
||||
|
||||
HEADERS += src/alohalytics.h \
|
||||
src/event_base.h \
|
||||
src/http_client.h \
|
||||
src/gzip_wrapper.h \
|
||||
src/logger.h \
|
||||
src/location.h \
|
||||
|
||||
QMAKE_LFLAGS *= -lz
|
||||
|
||||
macx-* {
|
||||
OBJECTIVE_SOURCES += src/http_client_apple.mm
|
||||
OBJECTIVE_SOURCES += src/apple/http_client_apple.mm
|
||||
QMAKE_OBJECTIVE_CFLAGS *= -fobjc-arc
|
||||
QMAKE_LFLAGS *= -framework Foundation
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ apply plugin: 'com.android.application'
|
|||
|
||||
dependencies {
|
||||
// This one is needed to get Google Play advertising ID if Google Play Services are available on the device.
|
||||
compile 'com.google.android.gms:play-services-location:+'
|
||||
compile 'com.google.android.gms:play-services-base:+'
|
||||
}
|
||||
|
||||
android {
|
||||
|
|
|
@ -2,10 +2,14 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="org.alohalytics.demoapp" >
|
||||
|
||||
<!-- This permission is needed if you want to access IMEI -->
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||
<!-- This one is needed to upload statistics to the server -->
|
||||
<!-- REQUIRED to upload statistics to the server -->
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<!-- OPTIONAL to access IMEI -->
|
||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||
<!-- OPTIONAL to access network state on install/update/launch events -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
<!-- OPTIONAL to access location on install/update/launch -->
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
@ -22,7 +26,7 @@
|
|||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Used to automatically upload statistics using WiFi. -->
|
||||
<!-- OPTIONAL to automatically upload statistics when connected to WiFi. -->
|
||||
<receiver
|
||||
android:name="org.alohalytics.ConnectivityChangedReceiver"
|
||||
android:enabled="true"
|
||||
|
|
|
@ -26,6 +26,8 @@ package org.alohalytics.demoapp;
|
|||
|
||||
import android.app.Activity;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.view.KeyEvent;
|
||||
|
@ -49,9 +51,9 @@ public class MainActivity extends Activity {
|
|||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
|
||||
Statistics.setup(STATISTICS_SERVER_URL, this);
|
||||
// Optionally enable debug mode for easier integration (but don't forget to remove it in production!).
|
||||
Statistics.setDebugMode(true);
|
||||
Statistics.setup(STATISTICS_SERVER_URL, this);
|
||||
|
||||
// To handle Enter key for convenience testing on emulator
|
||||
findViewById(R.id.eventNameEditor).setOnKeyListener(new View.OnKeyListener() {
|
||||
|
@ -94,6 +96,17 @@ public class MainActivity extends Activity {
|
|||
// Event with a key=value pairs but passed as an array.
|
||||
Statistics.logEvent("app", arr);
|
||||
|
||||
// Event with location.
|
||||
Location l = new Location(LocationManager.NETWORK_PROVIDER);
|
||||
l.setTime(1423169916587L);
|
||||
l.setAccuracy(0.3f);
|
||||
l.setLatitude(-84.123456789);
|
||||
l.setLongitude(177.123456789);
|
||||
l.setAltitude(-11);
|
||||
l.setSpeed(27.123456789f);
|
||||
l.setBearing(0.123456789f);
|
||||
Statistics.logEvent("location", new String[]{"key", "value"}, l);
|
||||
|
||||
// Example event to track user activity.
|
||||
Statistics.logEvent("$onResume", getLocalClassName());
|
||||
}
|
||||
|
|
|
@ -277,10 +277,10 @@ class FlagRegisterer : public FlagRegistererBase {
|
|||
const std::string description_;
|
||||
};
|
||||
|
||||
#define DEFINE_flag(type, name, default_value, description) \
|
||||
type FLAGS_##name = default_value; \
|
||||
::dflags::FlagRegisterer<type> REGISTERER_##name( \
|
||||
std::ref(FLAGS_##name), #name, #type, default_value, description)
|
||||
#define DEFINE_flag(type, name, default_value, description) \
|
||||
type FLAGS_##name = default_value; \
|
||||
::dflags::FlagRegisterer<type> REGISTERER_##name(std::ref(FLAGS_##name), #name, #type, default_value, \
|
||||
description)
|
||||
|
||||
#define DEFINE_int8(name, default_value, description) DEFINE_flag(int8_t, name, default_value, description)
|
||||
#define DEFINE_uint8(name, default_value, description) DEFINE_flag(uint8_t, name, default_value, description)
|
||||
|
|
|
@ -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
|
||||
|
@ -44,6 +44,7 @@ DEFINE_bool(debug, true, "Enables debug mode for statistics engine.");
|
|||
DEFINE_bool(upload, false, "If true, triggers event to forcebly upload all statistics to the server.");
|
||||
DEFINE_double(sleep, 1, "The number of seconds to sleep before terminating.");
|
||||
DEFINE_string(id, "0xDEADBABA", "Unique client id.");
|
||||
DEFINE_bool(location, true, "Simulates event with a location.");
|
||||
|
||||
using namespace std;
|
||||
using alohalytics::Stats;
|
||||
|
@ -103,6 +104,12 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
}
|
||||
|
||||
if (FLAGS_location) {
|
||||
alohalytics::Location location;
|
||||
location.SetLatLon(123456789L, -14.1234567, 133.1234567, 3.52);
|
||||
stats.LogEvent("SimulatedLocationEvent", {{"somekey", "somevalue"}}, location);
|
||||
}
|
||||
|
||||
if (FLAGS_upload) {
|
||||
stats.Upload();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -26,43 +26,26 @@
|
|||
|
||||
// This define is needed to preserve client's timestamps in events.
|
||||
#define ALOHALYTICS_SERVER
|
||||
#include "../event_base.h"
|
||||
#include "../../src/event_base.h"
|
||||
|
||||
#include "../Bricks/rtti/dispatcher.h"
|
||||
#include "../../src/Bricks/rtti/dispatcher.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <typeinfo>
|
||||
|
||||
// If you use only virtual functions in your events, you don't need any Processor at all.
|
||||
// Here we process some events with Processor for example only.
|
||||
struct Processor {
|
||||
void PrintTime(const AlohalyticsBaseEvent & event) {
|
||||
const time_t timestamp = static_cast<const time_t>(event.timestamp / 1000);
|
||||
std::cout << std::put_time(std::localtime(×tamp), "%e-%b-%Y %H:%M:%S");
|
||||
}
|
||||
void operator()(const AlohalyticsBaseEvent & event) {
|
||||
PrintTime(event);
|
||||
std::cout << "Unhandled event of type " << typeid(event).name() << std::endl;
|
||||
}
|
||||
void operator()(const AlohalyticsIdEvent & event) {
|
||||
PrintTime(event);
|
||||
std::cout << " ID: " << event.id << std::endl;
|
||||
}
|
||||
void operator()(const AlohalyticsKeyEvent & event) {
|
||||
PrintTime(event);
|
||||
std::cout << ' ' << event.key << std::endl;
|
||||
}
|
||||
void operator()(const AlohalyticsKeyValueEvent & event) {
|
||||
PrintTime(event);
|
||||
std::cout << ' ' << event.key << " = " << event.value << std::endl;
|
||||
}
|
||||
void operator()(const AlohalyticsKeyPairsEvent & event) {
|
||||
PrintTime(event);
|
||||
std::cout << ' ' << event.key << " [ ";
|
||||
for (const auto & pair : event.pairs) {
|
||||
std::cout << pair.first << '=' << pair.second << ' ';
|
||||
}
|
||||
std::cout << ']' << std::endl;
|
||||
void operator()(const AlohalyticsBaseEvent &event) {
|
||||
std::cout << "Unhandled event of type " << typeid(event).name() << " with timestamp " << event.ToString()
|
||||
<< std::endl;
|
||||
}
|
||||
void operator()(const AlohalyticsIdEvent &event) { std::cout << event.ToString() << std::endl; }
|
||||
void operator()(const AlohalyticsKeyEvent &event) { std::cout << event.ToString() << std::endl; }
|
||||
void operator()(const AlohalyticsKeyValueEvent &event) { std::cout << event.ToString() << std::endl; }
|
||||
void operator()(const AlohalyticsKeyPairsEvent &event) { std::cout << event.ToString() << std::endl; }
|
||||
void operator()(const AlohalyticsKeyPairsLocationEvent &event) { std::cout << event.ToString() << std::endl; }
|
||||
};
|
||||
|
||||
int main(int, char **) {
|
||||
|
@ -72,13 +55,11 @@ int main(int, char **) {
|
|||
while (std::cin.good()) {
|
||||
std::unique_ptr<AlohalyticsBaseEvent> ptr;
|
||||
ar(ptr);
|
||||
bricks::rtti::RuntimeDispatcher<AlohalyticsBaseEvent,
|
||||
AlohalyticsKeyPairsEvent,
|
||||
AlohalyticsIdEvent,
|
||||
AlohalyticsKeyValueEvent,
|
||||
bricks::rtti::RuntimeDispatcher<AlohalyticsBaseEvent, AlohalyticsKeyPairsLocationEvent,
|
||||
AlohalyticsKeyPairsEvent, AlohalyticsIdEvent, AlohalyticsKeyValueEvent,
|
||||
AlohalyticsKeyEvent>::DispatchCall(*ptr, processor);
|
||||
}
|
||||
} catch (const cereal::Exception & ex) {
|
||||
} catch (const cereal::Exception &) {
|
||||
}
|
||||
return 0;
|
||||
}
|
60
3party/Alohalytics/examples/server/gunzip.cc
Normal file
60
3party/Alohalytics/examples/server/gunzip.cc
Normal file
|
@ -0,0 +1,60 @@
|
|||
/*******************************************************************************
|
||||
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.
|
||||
*******************************************************************************/
|
||||
|
||||
// Small demo which ungzips and prints cereal stream from file.
|
||||
|
||||
// This define is needed to preserve client's timestamps in events.
|
||||
#define ALOHALYTICS_SERVER
|
||||
#include "../../src/event_base.h"
|
||||
#include "../../src/gzip_wrapper.h"
|
||||
|
||||
#include "../../src/Bricks/file/file.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <typeinfo>
|
||||
#include <fstream>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
if (argc < 2) {
|
||||
std::cout << "Usage: " << argv[0] << " <gzipped_cereal_file>" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
try {
|
||||
const std::string ungzipped = alohalytics::Gunzip(bricks::ReadFileAsString(argv[1]));
|
||||
std::istringstream in_stream(ungzipped);
|
||||
cereal::BinaryInputArchive in_archive(in_stream);
|
||||
std::unique_ptr<AlohalyticsBaseEvent> ptr;
|
||||
// Cereal can't detect the end of the stream without our help.
|
||||
// If tellg returns -1 we will exit safely.
|
||||
while (ungzipped.size() > static_cast<size_t>(in_stream.tellg())) {
|
||||
in_archive(ptr);
|
||||
std::cout << ptr->ToString() << std::endl;
|
||||
}
|
||||
} catch (const std::exception& ex) {
|
||||
std::cerr << "Exception: " << ex.what() << std::endl;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -413,7 +413,9 @@ class FSQ final : public CONFIG::T_FILE_NAMING_STRATEGY,
|
|||
if (next_file) {
|
||||
// const FileProcessingResult result = processor_.OnFileReady(*next_file.get(),
|
||||
// time_manager_.Now());
|
||||
const bool successfully_processed = processor_.OnFileReady(next_file->full_path_name, next_file->size);
|
||||
// AlexZ: we can't trust next_file->size here. Debugging shows that it can be greater than the real file size.
|
||||
// TODO: refactor FSQ or capture a bug.
|
||||
const bool successfully_processed = processor_.OnFileReady(next_file->full_path_name);
|
||||
// Important to clear force_processing_, in a locked way.
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(status_mutex_);
|
||||
|
|
|
@ -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
|
||||
|
@ -27,6 +27,7 @@
|
|||
|
||||
#include "message_queue.h"
|
||||
#include "FileStorageQueue/fsq.h"
|
||||
#include "location.h"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
@ -65,7 +66,7 @@ class Stats final {
|
|||
// Called by file storage engine to upload file with collected data.
|
||||
// Should return true if upload has been successful.
|
||||
// TODO(AlexZ): Refactor FSQ to make this method private.
|
||||
bool OnFileReady(const std::string& full_path_to_file, uint64_t file_size);
|
||||
bool OnFileReady(const std::string& full_path_to_file);
|
||||
|
||||
static Stats& Instance();
|
||||
|
||||
|
@ -82,26 +83,36 @@ class Stats final {
|
|||
Stats& SetClientId(const std::string& unique_client_id);
|
||||
|
||||
void LogEvent(std::string const& event_name);
|
||||
void LogEvent(std::string const& event_name, Location const& location);
|
||||
|
||||
void LogEvent(std::string const& event_name, std::string const& event_value);
|
||||
void LogEvent(std::string const& event_name, std::string const& event_value, Location const& location);
|
||||
|
||||
void LogEvent(std::string const& event_name, TStringMap const& value_pairs);
|
||||
void LogEvent(std::string const& event_name, TStringMap const& value_pairs, Location const& location);
|
||||
|
||||
// Forcedly tries to upload all stored data to the server.
|
||||
void Upload();
|
||||
};
|
||||
|
||||
inline void LogEvent(std::string const& event_name) {
|
||||
Stats::Instance().LogEvent(event_name);
|
||||
inline void LogEvent(std::string const& event_name) { Stats::Instance().LogEvent(event_name); }
|
||||
inline void LogEvent(std::string const& event_name, Location const& location) {
|
||||
Stats::Instance().LogEvent(event_name, location);
|
||||
}
|
||||
|
||||
inline void LogEvent(std::string const& event_name, std::string const& event_value) {
|
||||
Stats::Instance().LogEvent(event_name, event_value);
|
||||
}
|
||||
inline void LogEvent(std::string const& event_name, std::string const& event_value, Location const& location) {
|
||||
Stats::Instance().LogEvent(event_name, event_value, location);
|
||||
}
|
||||
|
||||
inline void LogEvent(std::string const& event_name, TStringMap const& value_pairs) {
|
||||
Stats::Instance().LogEvent(event_name, value_pairs);
|
||||
}
|
||||
inline void LogEvent(std::string const& event_name, TStringMap const& value_pairs, Location const& location) {
|
||||
Stats::Instance().LogEvent(event_name, value_pairs, location);
|
||||
}
|
||||
|
||||
} // namespace alohalytics
|
||||
|
||||
|
|
|
@ -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
|
||||
|
@ -26,6 +26,8 @@ package org.alohalytics;
|
|||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.location.Location;
|
||||
import android.location.LocationManager;
|
||||
import android.util.Pair;
|
||||
|
||||
import java.io.File;
|
||||
|
@ -49,15 +51,7 @@ public class Statistics {
|
|||
return sDebugModeEnabled;
|
||||
}
|
||||
|
||||
// Use this setup if you are releasing a new application and/or don't bother about already existing installations
|
||||
// prior to Alohalytics integration. Alohalytics will check new unique installations by internal logic only if use this function.
|
||||
public static void setup(final String serverUrl, final Context context) {
|
||||
setup(serverUrl, context, true);
|
||||
}
|
||||
|
||||
// Set firstAppLaunch to false if you definitely know that your app was previously installed
|
||||
// (before integrating with Alohalytics) to correctly calculate new unique installations.
|
||||
public static void setup(final String serverUrl, final Context context, boolean firstAppLaunch) {
|
||||
final String storagePath = context.getFilesDir().getAbsolutePath() + "/Alohalytics/";
|
||||
// Native code expects valid existing writable dir.
|
||||
(new File(storagePath)).mkdirs();
|
||||
|
@ -78,32 +72,69 @@ public class Statistics {
|
|||
ex.printStackTrace();
|
||||
}
|
||||
final SharedPreferences prefs = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
|
||||
Location lastKnownLocation = null;
|
||||
if (SystemInfo.hasPermission("android.permission.ACCESS_FINE_LOCATION", context)) {
|
||||
// Requires ACCESS_FINE_LOCATION permission.
|
||||
final LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
|
||||
lastKnownLocation = (lm == null) ? null : lm.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
|
||||
}
|
||||
// Is it a real new install?
|
||||
if (firstAppLaunch && id.second && installTime == updateTime) {
|
||||
if (id.second && installTime == updateTime) {
|
||||
logEvent("$install", new String[]{"version", versionName,
|
||||
"secondsBeforeLaunch", String.valueOf((System.currentTimeMillis() - installTime) / 1000)});
|
||||
"secondsBeforeLaunch", String.valueOf((System.currentTimeMillis() - installTime) / 1000)},
|
||||
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,
|
||||
"userAgeInSeconds", String.valueOf((System.currentTimeMillis() - installTime) / 1000)});
|
||||
"secondsBeforeLaunch", String.valueOf((System.currentTimeMillis() - updateTime) / 1000),
|
||||
"userAgeInSeconds", String.valueOf((System.currentTimeMillis() - installTime) / 1000)},
|
||||
lastKnownLocation);
|
||||
// Also collect device info on update.
|
||||
SystemInfo.getDeviceInfoAsync(context);
|
||||
prefs.edit().putLong(PREF_APP_UPDATE_TIME, updateTime).apply();
|
||||
}
|
||||
logEvent("$launch", SystemInfo.hasPermission("android.permission.ACCESS_NETWORK_STATE", context)
|
||||
? SystemInfo.getConnectionInfo(context) : null, lastKnownLocation);
|
||||
}
|
||||
|
||||
public static native void logEvent(String eventName);
|
||||
|
||||
public static native void logEvent(String eventName, String eventValue);
|
||||
|
||||
// eventDictionary is a key,value,key,value array.
|
||||
public static native void logEvent(String eventName, String[] eventDictionary);
|
||||
|
||||
private static native void logEvent(String eventName, String[] eventDictionary, boolean hasLatLon,
|
||||
long timestamp, double lat, double lon, float accuracy,
|
||||
boolean hasAltitude, double altitude, boolean hasBearing,
|
||||
float bearing, boolean hasSpeed, float speed, byte source);
|
||||
|
||||
public static void logEvent(String eventName, String[] eventDictionary, Location l) {
|
||||
if (l == null) {
|
||||
logEvent(eventName, eventDictionary);
|
||||
} else {
|
||||
logEvent("$launch");
|
||||
// See alohalytics::Location::Source in the location.h for enum constants.
|
||||
byte source = 0;
|
||||
switch (l.getProvider()) {
|
||||
case LocationManager.GPS_PROVIDER:
|
||||
source = 1;
|
||||
break;
|
||||
case LocationManager.NETWORK_PROVIDER:
|
||||
source = 2;
|
||||
break;
|
||||
case LocationManager.PASSIVE_PROVIDER:
|
||||
source = 3;
|
||||
break;
|
||||
}
|
||||
logEvent(eventName, eventDictionary, l.hasAccuracy(), l.getTime(), l.getLatitude(), l.getLongitude(),
|
||||
l.getAccuracy(), l.hasAltitude(), l.getAltitude(), l.hasBearing(), l.getBearing(),
|
||||
l.hasSpeed(), l.getSpeed(), source);
|
||||
}
|
||||
}
|
||||
|
||||
native static public void logEvent(String eventName);
|
||||
|
||||
native static public void logEvent(String eventName, String eventValue);
|
||||
|
||||
// eventDictionary is a key,value,key,value array.
|
||||
native static public void logEvent(String eventName, String[] eventDictionary);
|
||||
|
||||
static public void logEvent(String eventName, Map<String, String> eventDictionary) {
|
||||
public static void logEvent(String eventName, Map<String, String> eventDictionary) {
|
||||
// For faster native processing pass array of strings instead of a map.
|
||||
final String[] array = new String[eventDictionary.size() * 2];
|
||||
int index = 0;
|
||||
|
|
|
@ -26,7 +26,10 @@ package org.alohalytics;
|
|||
|
||||
import android.content.ContentResolver;
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
import android.telephony.TelephonyManager;
|
||||
|
@ -113,16 +116,20 @@ public class SystemInfo {
|
|||
handleException(ex);
|
||||
}
|
||||
|
||||
try {
|
||||
// This code works only if the app has READ_PHONE_STATE permission.
|
||||
final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
ids.put("device_id", tm.getDeviceId());
|
||||
ids.put("sim_serial_number", tm.getSimSerialNumber());
|
||||
} catch (Exception ex) {
|
||||
handleException(ex);
|
||||
if (SystemInfo.hasPermission("android.permission.READ_PHONE_STATE", context)) {
|
||||
try {
|
||||
// This code works only if the app has READ_PHONE_STATE permission.
|
||||
final TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
|
||||
ids.put("device_id", tm.getDeviceId());
|
||||
ids.put("sim_serial_number", tm.getSimSerialNumber());
|
||||
} catch (Exception ex) {
|
||||
handleException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -227,4 +234,38 @@ public class SystemInfo {
|
|||
Statistics.logEvent("$androidDeviceInfo", kvs.mPairs);
|
||||
}
|
||||
|
||||
// Requires ACCESS_NETWORK_STATE permission.
|
||||
public static String[] getConnectionInfo(final Context context) {
|
||||
final ConnectivityManager cm = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
if (cm == null) {
|
||||
return new String[]{"null", "cm"};
|
||||
}
|
||||
final NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
|
||||
if (activeNetwork == null) {
|
||||
return new String[]{"null", "activeNetwork"};
|
||||
}
|
||||
final boolean isConnected = activeNetwork.isConnected();
|
||||
final boolean isRoaming = activeNetwork.isRoaming();
|
||||
String type = "unknown";
|
||||
switch (activeNetwork.getType()) {
|
||||
case ConnectivityManager.TYPE_BLUETOOTH: type = "bluetooth"; break;
|
||||
case ConnectivityManager.TYPE_DUMMY: type = "dummy"; break;
|
||||
case ConnectivityManager.TYPE_ETHERNET: type = "ethernet"; break;
|
||||
case ConnectivityManager.TYPE_MOBILE: type = "mobile"; break;
|
||||
case ConnectivityManager.TYPE_MOBILE_DUN: type = "dun"; break;
|
||||
case ConnectivityManager.TYPE_MOBILE_HIPRI: type = "hipri"; break;
|
||||
case ConnectivityManager.TYPE_MOBILE_MMS: type = "mms"; break;
|
||||
case ConnectivityManager.TYPE_MOBILE_SUPL: type = "supl"; break;
|
||||
case ConnectivityManager.TYPE_VPN: type = "vpn"; break;
|
||||
case ConnectivityManager.TYPE_WIFI: type = "wifi"; break;
|
||||
case ConnectivityManager.TYPE_WIMAX: type = "wimax"; break;
|
||||
}
|
||||
return new String[]{"connected", isConnected ? "yes" : "no",
|
||||
"roaming", isRoaming ? "yes" : "no",
|
||||
"ctype", type};
|
||||
}
|
||||
|
||||
public static boolean hasPermission(final String permission, final Context context) {
|
||||
return PackageManager.PERMISSION_GRANTED == context.checkCallingOrSelfPermission(permission);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -40,6 +40,8 @@ extern JavaVM* GetJVM();
|
|||
|
||||
namespace {
|
||||
|
||||
static constexpr double kDefaultAndroidVerticalAccuracy = 0.0;
|
||||
|
||||
template <typename POINTER, typename DELETER>
|
||||
unique_ptr<POINTER, DELETER> MakePointerScopeGuard(POINTER* x, DELETER t) {
|
||||
return unique_ptr<POINTER, DELETER>(x, t);
|
||||
|
@ -81,6 +83,28 @@ string ToStdString(JNIEnv* env, jstring str) {
|
|||
return result;
|
||||
}
|
||||
|
||||
// keyValuePairs can be null!
|
||||
TStringMap FillMapHelper(JNIEnv* env, jobjectArray keyValuePairs) {
|
||||
TStringMap map;
|
||||
if (keyValuePairs) {
|
||||
const jsize count = env->GetArrayLength(keyValuePairs);
|
||||
string key;
|
||||
for (jsize i = 0; i < count; ++i) {
|
||||
const jstring jni_string = static_cast<jstring>(env->GetObjectArrayElement(keyValuePairs, i));
|
||||
if ((i + 1) % 2) {
|
||||
key = ToStdString(env, jni_string);
|
||||
map[key] = "";
|
||||
} else {
|
||||
map[key] = ToStdString(env, jni_string);
|
||||
}
|
||||
if (jni_string) {
|
||||
env->DeleteLocalRef(jni_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
@ -96,20 +120,43 @@ JNIEXPORT void JNICALL Java_org_alohalytics_Statistics_logEvent__Ljava_lang_Stri
|
|||
|
||||
JNIEXPORT void JNICALL Java_org_alohalytics_Statistics_logEvent__Ljava_lang_String_2_3Ljava_lang_String_2(
|
||||
JNIEnv* env, jclass, jstring eventName, jobjectArray keyValuePairs) {
|
||||
const jsize count = env->GetArrayLength(keyValuePairs);
|
||||
TStringMap map;
|
||||
string key;
|
||||
for (jsize i = 0; i < count; ++i) {
|
||||
const jstring jni_string = static_cast<jstring>(env->GetObjectArrayElement(keyValuePairs, i));
|
||||
if ((i + 1) % 2) {
|
||||
key = ToStdString(env, jni_string);
|
||||
map[key] = "";
|
||||
} else {
|
||||
map[key] = ToStdString(env, jni_string);
|
||||
}
|
||||
if (jni_string) env->DeleteLocalRef(jni_string);
|
||||
LogEvent(ToStdString(env, eventName), FillMapHelper(env, keyValuePairs));
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_org_alohalytics_Statistics_logEvent__Ljava_lang_String_2_3Ljava_lang_String_2ZJDDFZDZFZFB(
|
||||
JNIEnv* env,
|
||||
jclass,
|
||||
jstring eventName,
|
||||
jobjectArray keyValuePairs,
|
||||
jboolean hasLatLon,
|
||||
jlong timestamp,
|
||||
jdouble lat,
|
||||
jdouble lon,
|
||||
jfloat accuracy,
|
||||
jboolean hasAltitude,
|
||||
jdouble altitude,
|
||||
jboolean hasBearing,
|
||||
jfloat bearing,
|
||||
jboolean hasSpeed,
|
||||
jfloat speed,
|
||||
jbyte source) {
|
||||
alohalytics::Location l;
|
||||
if (hasLatLon) {
|
||||
l.SetLatLon(timestamp, lat, lon, accuracy);
|
||||
l.SetSource((alohalytics::Location::Source)source);
|
||||
}
|
||||
LogEvent(ToStdString(env, eventName), map);
|
||||
if (hasAltitude) {
|
||||
l.SetAltitude(altitude, kDefaultAndroidVerticalAccuracy);
|
||||
}
|
||||
if (hasBearing) {
|
||||
l.SetBearing(bearing);
|
||||
}
|
||||
if (hasSpeed) {
|
||||
l.SetSpeed(speed);
|
||||
}
|
||||
|
||||
LogEvent(ToStdString(env, eventName), FillMapHelper(env, keyValuePairs), l);
|
||||
}
|
||||
|
||||
#define CLEAR_AND_RETURN_FALSE_ON_EXCEPTION \
|
||||
|
@ -134,8 +181,7 @@ JNIEXPORT void JNICALL Java_org_alohalytics_Statistics_setupCPP(JNIEnv* env,
|
|||
g_httpTransportClass = static_cast<jclass>(env->NewGlobalRef(httpTransportClass));
|
||||
RETURN_ON_EXCEPTION
|
||||
g_httpTransportClass_run =
|
||||
env->GetStaticMethodID(g_httpTransportClass,
|
||||
"run",
|
||||
env->GetStaticMethodID(g_httpTransportClass, "run",
|
||||
"(Lorg/alohalytics/HttpTransport$Params;)Lorg/alohalytics/HttpTransport$Params;");
|
||||
RETURN_ON_EXCEPTION
|
||||
g_httpParamsClass = env->FindClass("org/alohalytics/HttpTransport$Params");
|
||||
|
@ -203,8 +249,8 @@ bool HTTPClientPlatformWrapper::RunHTTPRequest() {
|
|||
const auto jniPostData = MakePointerScopeGuard(env->NewByteArray(post_body_.size()), deleteLocalRef);
|
||||
CLEAR_AND_RETURN_FALSE_ON_EXCEPTION
|
||||
|
||||
env->SetByteArrayRegion(
|
||||
jniPostData.get(), 0, post_body_.size(), reinterpret_cast<const jbyte*>(post_body_.data()));
|
||||
env->SetByteArrayRegion(jniPostData.get(), 0, post_body_.size(),
|
||||
reinterpret_cast<const jbyte*>(post_body_.data()));
|
||||
CLEAR_AND_RETURN_FALSE_ON_EXCEPTION
|
||||
|
||||
env->SetObjectField(httpParamsObject.get(), dataField, jniPostData.get());
|
||||
|
@ -255,8 +301,7 @@ bool HTTPClientPlatformWrapper::RunHTTPRequest() {
|
|||
CLEAR_AND_RETURN_FALSE_ON_EXCEPTION
|
||||
}
|
||||
|
||||
const static jfieldID debugModeField =
|
||||
env->GetFieldID(g_httpParamsClass, "debugMode", "Z");
|
||||
const static jfieldID debugModeField = env->GetFieldID(g_httpParamsClass, "debugMode", "Z");
|
||||
env->SetBooleanField(httpParamsObject.get(), debugModeField, debug_mode_);
|
||||
CLEAR_AND_RETURN_FALSE_ON_EXCEPTION
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
@ -36,7 +36,10 @@
|
|||
#include "../cereal/include/types/string.hpp"
|
||||
#include "../cereal/include/types/map.hpp"
|
||||
|
||||
#define LOG_IF_DEBUG(...) if (debug_mode_) { alohalytics::Logger().Log(__VA_ARGS__); }
|
||||
#define LOG_IF_DEBUG(...) \
|
||||
if (debug_mode_) { \
|
||||
alohalytics::Logger().Log(__VA_ARGS__); \
|
||||
}
|
||||
|
||||
namespace alohalytics {
|
||||
|
||||
|
@ -46,7 +49,7 @@ struct NoOpDeleter {
|
|||
void operator()(T*) {}
|
||||
};
|
||||
|
||||
// Use alohalytics::Stats::Instance() to access statistics engine.
|
||||
// Use alohalytics::Stats::Instance() to access statistics engine.
|
||||
Stats::Stats() : message_queue_(*this) {}
|
||||
|
||||
bool Stats::UploadBuffer(const std::string& url, std::string&& buffer, bool debug_mode) {
|
||||
|
@ -55,7 +58,7 @@ bool Stats::UploadBuffer(const std::string& url, std::string&& buffer, bool debu
|
|||
request.set_post_body(std::move(buffer), "application/alohalytics-binary-blob");
|
||||
|
||||
try {
|
||||
return request.RunHTTPRequest() && 200 == request.error_code();
|
||||
return request.RunHTTPRequest() && 200 == request.error_code() && !request.was_redirected();
|
||||
} catch (const std::exception& ex) {
|
||||
if (debug_mode) {
|
||||
ALOG("Exception while trying to upload file", ex.what());
|
||||
|
@ -85,15 +88,21 @@ void Stats::OnMessage(const std::string& message, size_t dropped_events) {
|
|||
// Called by file storage engine to upload file with collected data.
|
||||
// Should return true if upload has been successful.
|
||||
// TODO(AlexZ): Refactor FSQ to make this method private.
|
||||
bool Stats::OnFileReady(const std::string& full_path_to_file, uint64_t file_size) {
|
||||
bool Stats::OnFileReady(const std::string& full_path_to_file) {
|
||||
if (upload_url_.empty()) {
|
||||
LOG_IF_DEBUG("Warning: upload server url was not set and file of", file_size, "bytes was not uploaded.");
|
||||
LOG_IF_DEBUG("Warning: upload server url was not set and file", full_path_to_file, "was not uploaded.");
|
||||
return false;
|
||||
}
|
||||
if (unique_client_id_event_.empty()) {
|
||||
LOG_IF_DEBUG("Warning: unique client ID is not set so statistics was not uploaded.");
|
||||
return false;
|
||||
}
|
||||
// TODO(AlexZ): Refactor to use crossplatform and safe/non-throwing version.
|
||||
const uint64_t file_size = bricks::FileSystem::GetFileSize(full_path_to_file);
|
||||
if (0 == file_size) {
|
||||
LOG_IF_DEBUG("ERROR: Trying to upload file of size 0?", full_path_to_file);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG_IF_DEBUG("Trying to upload statistics file", full_path_to_file, "to", upload_url_);
|
||||
|
||||
|
@ -145,8 +154,8 @@ Stats& Stats::SetStoragePath(const std::string& full_path_to_storage_with_a_slas
|
|||
LOG_IF_DEBUG("Active file size:", status.appended_file_size);
|
||||
const size_t count = status.finalized.queue.size();
|
||||
if (count) {
|
||||
LOG_IF_DEBUG(
|
||||
count, "files with total size of", status.finalized.total_size, "bytes are waiting for upload.");
|
||||
LOG_IF_DEBUG(count, "files with total size of", status.finalized.total_size,
|
||||
"bytes are waiting for upload.");
|
||||
}
|
||||
}
|
||||
const size_t memory_events_count = memory_storage_.size();
|
||||
|
@ -176,16 +185,28 @@ Stats& Stats::SetClientId(const std::string& unique_client_id) {
|
|||
return *this;
|
||||
}
|
||||
|
||||
static inline void LogEventImpl(AlohalyticsBaseEvent const& event, MessageQueue<Stats>& msq) {
|
||||
std::ostringstream sstream;
|
||||
{
|
||||
// unique_ptr is used to correctly serialize polymorphic types.
|
||||
cereal::BinaryOutputArchive(sstream) << std::unique_ptr<AlohalyticsBaseEvent const, NoOpDeleter>(&event);
|
||||
}
|
||||
msq.PushMessage(std::move(sstream.str()));
|
||||
}
|
||||
|
||||
void Stats::LogEvent(std::string const& event_name) {
|
||||
LOG_IF_DEBUG("LogEvent:", event_name);
|
||||
AlohalyticsKeyEvent event;
|
||||
event.key = event_name;
|
||||
std::ostringstream sstream;
|
||||
{
|
||||
// unique_ptr is used to correctly serialize polymorphic types.
|
||||
cereal::BinaryOutputArchive(sstream) << std::unique_ptr<AlohalyticsBaseEvent, NoOpDeleter>(&event);
|
||||
}
|
||||
message_queue_.PushMessage(std::move(sstream.str()));
|
||||
LogEventImpl(event, message_queue_);
|
||||
}
|
||||
|
||||
void Stats::LogEvent(std::string const& event_name, Location const& location) {
|
||||
LOG_IF_DEBUG("LogEvent:", event_name, location.ToDebugString());
|
||||
AlohalyticsKeyLocationEvent event;
|
||||
event.key = event_name;
|
||||
event.location = location;
|
||||
LogEventImpl(event, message_queue_);
|
||||
}
|
||||
|
||||
void Stats::LogEvent(std::string const& event_name, std::string const& event_value) {
|
||||
|
@ -193,9 +214,16 @@ void Stats::LogEvent(std::string const& event_name, std::string const& event_val
|
|||
AlohalyticsKeyValueEvent event;
|
||||
event.key = event_name;
|
||||
event.value = event_value;
|
||||
std::ostringstream sstream;
|
||||
{ cereal::BinaryOutputArchive(sstream) << std::unique_ptr<AlohalyticsBaseEvent, NoOpDeleter>(&event); }
|
||||
message_queue_.PushMessage(std::move(sstream.str()));
|
||||
LogEventImpl(event, message_queue_);
|
||||
}
|
||||
|
||||
void Stats::LogEvent(std::string const& event_name, std::string const& event_value, Location const& location) {
|
||||
LOG_IF_DEBUG("LogEvent:", event_name, "=", event_value, location.ToDebugString());
|
||||
AlohalyticsKeyValueLocationEvent event;
|
||||
event.key = event_name;
|
||||
event.value = event_value;
|
||||
event.location = location;
|
||||
LogEventImpl(event, message_queue_);
|
||||
}
|
||||
|
||||
void Stats::LogEvent(std::string const& event_name, TStringMap const& value_pairs) {
|
||||
|
@ -203,9 +231,16 @@ void Stats::LogEvent(std::string const& event_name, TStringMap const& value_pair
|
|||
AlohalyticsKeyPairsEvent event;
|
||||
event.key = event_name;
|
||||
event.pairs = value_pairs;
|
||||
std::ostringstream sstream;
|
||||
{ cereal::BinaryOutputArchive(sstream) << std::unique_ptr<AlohalyticsBaseEvent, NoOpDeleter>(&event); }
|
||||
message_queue_.PushMessage(std::move(sstream.str()));
|
||||
LogEventImpl(event, message_queue_);
|
||||
}
|
||||
|
||||
void Stats::LogEvent(std::string const& event_name, TStringMap const& value_pairs, Location const& location) {
|
||||
LOG_IF_DEBUG("LogEvent:", event_name, "=", value_pairs, location.ToDebugString());
|
||||
AlohalyticsKeyPairsLocationEvent event;
|
||||
event.key = event_name;
|
||||
event.pairs = value_pairs;
|
||||
event.location = location;
|
||||
LogEventImpl(event, message_queue_);
|
||||
}
|
||||
|
||||
// Forcedly tries to upload all stored data to the server.
|
||||
|
|
|
@ -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
|
||||
|
@ -27,8 +27,8 @@
|
|||
|
||||
// Define ALOHALYTICS_SERVER when using this header on a server-side.
|
||||
|
||||
#include <string>
|
||||
#include <chrono>
|
||||
#include <sstream>
|
||||
|
||||
#include "cereal/include/cereal.hpp"
|
||||
#include "cereal/include/types/base_class.hpp"
|
||||
|
@ -37,10 +37,22 @@
|
|||
#include "cereal/include/types/string.hpp"
|
||||
#include "cereal/include/types/map.hpp"
|
||||
|
||||
#include "location.h"
|
||||
|
||||
// For easier processing on a server side, every statistics event should derive from this base class.
|
||||
struct AlohalyticsBaseEvent {
|
||||
uint64_t timestamp;
|
||||
|
||||
virtual std::string ToString() const {
|
||||
const time_t timet = static_cast<const time_t>(timestamp / 1000);
|
||||
char buf[100];
|
||||
if (::strftime(buf, 100, "%e-%b-%Y %H:%M:%S", ::gmtime(&timet))) {
|
||||
return buf;
|
||||
} else {
|
||||
return "INVALID_TIME";
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t CurrentTimestamp() {
|
||||
return std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
||||
|
@ -59,55 +71,119 @@ struct AlohalyticsBaseEvent {
|
|||
// To use polymorphism on a server side.
|
||||
virtual ~AlohalyticsBaseEvent() = default;
|
||||
};
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsBaseEvent, "b");
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsBaseEvent, "b")
|
||||
|
||||
// Special event in the beginning of each block (file) sent to stats server.
|
||||
// Designed to mark all events in this data block as belonging to one user with specified id.
|
||||
struct AlohalyticsIdEvent : public AlohalyticsBaseEvent {
|
||||
std::string id;
|
||||
|
||||
virtual std::string ToString() const { return AlohalyticsBaseEvent::ToString() + " ID: " + id; }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar) {
|
||||
AlohalyticsBaseEvent::serialize(ar);
|
||||
ar(CEREAL_NVP(id));
|
||||
}
|
||||
};
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsIdEvent, "i");
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsIdEvent, "i")
|
||||
|
||||
// Simple event with a string name (key) only.
|
||||
struct AlohalyticsKeyEvent : public AlohalyticsBaseEvent {
|
||||
std::string key;
|
||||
|
||||
virtual std::string ToString() const { return AlohalyticsBaseEvent::ToString() + " " + key; }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar) {
|
||||
AlohalyticsBaseEvent::serialize(ar);
|
||||
ar(CEREAL_NVP(key));
|
||||
}
|
||||
};
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsKeyEvent, "k");
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsKeyEvent, "k")
|
||||
|
||||
// Simple event with a string key/value pair.
|
||||
struct AlohalyticsKeyValueEvent : public AlohalyticsKeyEvent {
|
||||
std::string value;
|
||||
|
||||
virtual std::string ToString() const { return AlohalyticsKeyEvent::ToString() + " = " + value; }
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar) {
|
||||
AlohalyticsKeyEvent::serialize(ar);
|
||||
ar(CEREAL_NVP(value));
|
||||
}
|
||||
};
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsKeyValueEvent, "v");
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsKeyValueEvent, "v")
|
||||
|
||||
// Simple event with a string key and map<string, string> value.
|
||||
struct AlohalyticsKeyPairsEvent : public AlohalyticsKeyEvent {
|
||||
std::map<std::string, std::string> pairs;
|
||||
|
||||
virtual std::string ToString() const {
|
||||
std::ostringstream stream;
|
||||
stream << AlohalyticsKeyEvent::ToString() << " [ ";
|
||||
for (const auto& pair : pairs) {
|
||||
stream << pair.first << '=' << pair.second << ' ';
|
||||
}
|
||||
stream << ']';
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar) {
|
||||
AlohalyticsKeyEvent::serialize(ar);
|
||||
ar(CEREAL_NVP(pairs));
|
||||
}
|
||||
};
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsKeyPairsEvent, "p");
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsKeyPairsEvent, "p")
|
||||
|
||||
// Key + location.
|
||||
struct AlohalyticsKeyLocationEvent : public AlohalyticsKeyEvent {
|
||||
alohalytics::Location location;
|
||||
|
||||
virtual std::string ToString() const {
|
||||
return AlohalyticsKeyEvent::ToString() + ' ' + location.ToDebugString();
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar) {
|
||||
AlohalyticsKeyEvent::serialize(ar);
|
||||
ar(CEREAL_NVP(location));
|
||||
}
|
||||
};
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsKeyLocationEvent, "kl")
|
||||
|
||||
// Key + value + location.
|
||||
struct AlohalyticsKeyValueLocationEvent : public AlohalyticsKeyValueEvent {
|
||||
alohalytics::Location location;
|
||||
|
||||
virtual std::string ToString() const {
|
||||
return AlohalyticsKeyValueEvent::ToString() + ' ' + location.ToDebugString();
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar) {
|
||||
AlohalyticsKeyValueEvent::serialize(ar);
|
||||
ar(CEREAL_NVP(location));
|
||||
}
|
||||
};
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsKeyValueLocationEvent, "vl")
|
||||
|
||||
// Key + pairs of key/value + location.
|
||||
struct AlohalyticsKeyPairsLocationEvent : public AlohalyticsKeyPairsEvent {
|
||||
alohalytics::Location location;
|
||||
|
||||
virtual std::string ToString() const {
|
||||
return AlohalyticsKeyPairsEvent::ToString() + ' ' + location.ToDebugString();
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void serialize(Archive& ar) {
|
||||
AlohalyticsKeyPairsEvent::serialize(ar);
|
||||
ar(CEREAL_NVP(location));
|
||||
}
|
||||
};
|
||||
CEREAL_REGISTER_TYPE_WITH_NAME(AlohalyticsKeyPairsLocationEvent, "pl")
|
||||
|
||||
#endif // EVENT_BASE_H
|
||||
|
|
|
@ -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
|
||||
|
@ -22,28 +22,48 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|||
SOFTWARE.
|
||||
*******************************************************************************/
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <zlib.h>
|
||||
|
||||
#include "logger.h"
|
||||
|
||||
namespace alohalytics {
|
||||
|
||||
// Returns empty string on error.
|
||||
inline std::string Gzip(const std::string& data_to_compress) {
|
||||
static constexpr size_t kGzipBufferSize = 32768;
|
||||
|
||||
struct GzipErrorException : public std::exception {
|
||||
int err_;
|
||||
std::string msg_;
|
||||
GzipErrorException(int err, const char* msg) : err_(err), msg_(msg ? msg : "") {}
|
||||
virtual char const* what() const noexcept {
|
||||
return ("ERROR " + std::to_string(err_) + " while gzipping with zlib. " + msg_).c_str();
|
||||
}
|
||||
};
|
||||
|
||||
struct GunzipErrorException : public std::exception {
|
||||
int err_;
|
||||
std::string msg_;
|
||||
GunzipErrorException(int err, const char* msg) : err_(err), msg_(msg ? msg : "") {}
|
||||
virtual char const* what() const noexcept {
|
||||
return ("ERROR " + std::to_string(err_) + " while gunzipping with zlib. " + msg_).c_str();
|
||||
}
|
||||
};
|
||||
|
||||
inline std::string Gzip(const std::string& data_to_compress) throw(GzipErrorException) {
|
||||
z_stream z = {};
|
||||
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();
|
||||
std::string compressed;
|
||||
static const size_t kGzipBufferSize = 32768;
|
||||
char buffer[std::min(data_to_compress.size(), kGzipBufferSize)];
|
||||
std::vector<Bytef> buffer;
|
||||
buffer.resize(std::min(data_to_compress.size(), kGzipBufferSize));
|
||||
do {
|
||||
z.next_out = reinterpret_cast<Bytef*>(buffer);
|
||||
z.avail_out = sizeof(buffer)/sizeof(buffer[0]);
|
||||
z.next_out = buffer.data();
|
||||
z.avail_out = buffer.size();
|
||||
res = ::deflate(&z, Z_FINISH);
|
||||
if (compressed.size() < z.total_out) {
|
||||
compressed.append(buffer, z.total_out - compressed.size());
|
||||
compressed.append(reinterpret_cast<const char*>(buffer.data()), z.total_out - compressed.size());
|
||||
}
|
||||
} while (Z_OK == res);
|
||||
::deflateEnd(&z);
|
||||
|
@ -51,8 +71,32 @@ inline std::string Gzip(const std::string& data_to_compress) {
|
|||
return compressed;
|
||||
}
|
||||
}
|
||||
ALOG("ERROR", res, "while gzipping with zlib.", z.msg ? z.msg : "");
|
||||
return std::string();
|
||||
throw GzipErrorException(res, z.msg);
|
||||
}
|
||||
|
||||
} // namespace alohalytics
|
||||
inline std::string Gunzip(const std::string& data_to_decompress) throw(GzipErrorException) {
|
||||
z_stream z = {};
|
||||
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();
|
||||
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();
|
||||
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());
|
||||
}
|
||||
} while (Z_OK == res);
|
||||
::inflateEnd(&z);
|
||||
if (Z_STREAM_END == res) {
|
||||
return decompressed;
|
||||
}
|
||||
}
|
||||
throw GunzipErrorException(res, z.msg);
|
||||
}
|
||||
|
||||
} // namespace alohalytics
|
||||
|
|
|
@ -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
|
||||
|
|
277
3party/Alohalytics/src/location.h
Normal file
277
3party/Alohalytics/src/location.h
Normal file
|
@ -0,0 +1,277 @@
|
|||
/*******************************************************************************
|
||||
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.
|
||||
*******************************************************************************/
|
||||
|
||||
#ifndef LOCATION_H
|
||||
#define LOCATION_H
|
||||
|
||||
#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ != __ORDER_LITTLE_ENDIAN__)
|
||||
#error "This code does not support serialization on Big Endian archs (see TODOs below)."
|
||||
#endif
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
#include <sstream> // For ToDebugString()
|
||||
#include <exception>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
namespace alohalytics {
|
||||
|
||||
class Location {
|
||||
enum Mask : uint8_t {
|
||||
NOT_INITIALIZED = 0,
|
||||
HAS_LATLON = 1 << 0,
|
||||
HAS_ALTITUDE = 1 << 1,
|
||||
HAS_BEARING = 1 << 2,
|
||||
HAS_SPEED = 1 << 3,
|
||||
HAS_SOURCE = 1 << 4
|
||||
} valid_values_mask_ = NOT_INITIALIZED;
|
||||
|
||||
public:
|
||||
class LocationDecodeException : public std::exception {};
|
||||
|
||||
// Milliseconds from January 1, 1970.
|
||||
uint64_t timestamp_ms_;
|
||||
double latitude_deg_;
|
||||
double longitude_deg_;
|
||||
double horizontal_accuracy_m_;
|
||||
double altitude_m_;
|
||||
double vertical_accuracy_m_;
|
||||
// Positive degrees from the true North.
|
||||
double bearing_deg_;
|
||||
// Meters per second.
|
||||
double speed_mps_;
|
||||
|
||||
// We use degrees with precision of 7 decimal places - it's approx 2cm precision on the Earth.
|
||||
static constexpr double TEN_MILLION = 10000000.;
|
||||
// Some params below can be stored with 2 decimal places precision.
|
||||
static constexpr double ONE_HUNDRED = 100.;
|
||||
|
||||
// Compacts location into the byte representation.
|
||||
// TODO(AlexZ): We don't care about endiannes for now.
|
||||
std::string Encode() const {
|
||||
std::string s;
|
||||
s.push_back(valid_values_mask_);
|
||||
if (valid_values_mask_ & HAS_LATLON) {
|
||||
static_assert(sizeof(timestamp_ms_) == 8, "We cut off timestamp from 8 bytes to 6 to save space.");
|
||||
AppendToStringAsBinary(s, timestamp_ms_, sizeof(timestamp_ms_) - 2);
|
||||
const int32_t lat10mil = latitude_deg_ * TEN_MILLION;
|
||||
AppendToStringAsBinary(s, lat10mil);
|
||||
const int32_t lon10mil = longitude_deg_ * TEN_MILLION;
|
||||
AppendToStringAsBinary(s, lon10mil);
|
||||
const uint32_t horizontal_accuracy_cm = horizontal_accuracy_m_ * ONE_HUNDRED;
|
||||
AppendToStringAsBinary(s, horizontal_accuracy_cm);
|
||||
if (valid_values_mask_ & HAS_SOURCE) {
|
||||
s.push_back(source_);
|
||||
}
|
||||
}
|
||||
if (valid_values_mask_ & HAS_ALTITUDE) {
|
||||
const int32_t altitude_cm = altitude_m_ * ONE_HUNDRED;
|
||||
AppendToStringAsBinary(s, altitude_cm);
|
||||
const uint16_t vertical_accuracy_cm = vertical_accuracy_m_ * ONE_HUNDRED;
|
||||
AppendToStringAsBinary(s, vertical_accuracy_cm);
|
||||
}
|
||||
if (valid_values_mask_ & HAS_BEARING) {
|
||||
const uint32_t bearing10mil = bearing_deg_ * TEN_MILLION;
|
||||
AppendToStringAsBinary(s, bearing10mil);
|
||||
}
|
||||
if (valid_values_mask_ & HAS_SPEED) {
|
||||
const uint16_t speedx100mps = speed_mps_ * ONE_HUNDRED;
|
||||
AppendToStringAsBinary(s, speedx100mps);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
// Initializes location from serialized byte array created by ToString() method.
|
||||
// TODO(AlexZ): we don't care about endianness right now.
|
||||
explicit Location(const std::string &encoded) { Decode(encoded); }
|
||||
|
||||
void Decode(const std::string &encoded) {
|
||||
if (encoded.empty()) {
|
||||
throw LocationDecodeException();
|
||||
}
|
||||
std::string::size_type i = 0;
|
||||
const std::string::size_type size = encoded.size();
|
||||
valid_values_mask_ = static_cast<Mask>(encoded[i++]);
|
||||
if (valid_values_mask_ & HAS_LATLON) {
|
||||
if ((size - i) < 18) {
|
||||
throw LocationDecodeException();
|
||||
}
|
||||
timestamp_ms_ = *reinterpret_cast<const uint32_t *>(&encoded[i]) |
|
||||
(static_cast<uint64_t>(*reinterpret_cast<const uint16_t *>(&encoded[i + 4])) << 32);
|
||||
i += sizeof(uint64_t) - 2; // We use 6 bytes to store timestamps.
|
||||
latitude_deg_ = *reinterpret_cast<const int32_t *>(&encoded[i]) / TEN_MILLION;
|
||||
i += sizeof(int32_t);
|
||||
longitude_deg_ = *reinterpret_cast<const int32_t *>(&encoded[i]) / TEN_MILLION;
|
||||
i += sizeof(int32_t);
|
||||
horizontal_accuracy_m_ = *reinterpret_cast<const uint32_t *>(&encoded[i]) / ONE_HUNDRED;
|
||||
i += sizeof(uint32_t);
|
||||
if (valid_values_mask_ & HAS_SOURCE) {
|
||||
if ((size - i) < 1) {
|
||||
throw LocationDecodeException();
|
||||
}
|
||||
source_ = static_cast<Source>(encoded[i++]);
|
||||
}
|
||||
}
|
||||
if (valid_values_mask_ & HAS_ALTITUDE) {
|
||||
if ((size - i) < 6) {
|
||||
throw LocationDecodeException();
|
||||
}
|
||||
altitude_m_ = *reinterpret_cast<const int32_t *>(&encoded[i]) / ONE_HUNDRED;
|
||||
i += sizeof(int32_t);
|
||||
vertical_accuracy_m_ = *reinterpret_cast<const uint16_t *>(&encoded[i]) / ONE_HUNDRED;
|
||||
i += sizeof(uint16_t);
|
||||
}
|
||||
if (valid_values_mask_ & HAS_BEARING) {
|
||||
if ((size - i) < 4) {
|
||||
throw LocationDecodeException();
|
||||
}
|
||||
bearing_deg_ = *reinterpret_cast<const uint32_t *>(&encoded[i]) / TEN_MILLION;
|
||||
i += sizeof(uint32_t);
|
||||
}
|
||||
if (valid_values_mask_ & HAS_SPEED) {
|
||||
if ((size - i) < 2) {
|
||||
throw LocationDecodeException();
|
||||
}
|
||||
speed_mps_ = *reinterpret_cast<const uint16_t *>(&encoded[i]) / ONE_HUNDRED;
|
||||
i += sizeof(uint16_t);
|
||||
}
|
||||
}
|
||||
|
||||
Location() = default;
|
||||
|
||||
enum Source : std::uint8_t { UNKNOWN = 0, GPS = 1, NETWORK = 2, PASSIVE = 3 } source_;
|
||||
|
||||
bool HasLatLon() const { return valid_values_mask_ & HAS_LATLON; }
|
||||
Location &SetLatLon(uint64_t timestamp_ms,
|
||||
double latitude_deg,
|
||||
double longitude_deg,
|
||||
double horizontal_accuracy_m) {
|
||||
// We do not support values without known horizontal accuracy.
|
||||
if (horizontal_accuracy_m > 0.0) {
|
||||
timestamp_ms_ = timestamp_ms;
|
||||
latitude_deg_ = latitude_deg;
|
||||
longitude_deg_ = longitude_deg;
|
||||
horizontal_accuracy_m_ = horizontal_accuracy_m;
|
||||
valid_values_mask_ = static_cast<Mask>(valid_values_mask_ | HAS_LATLON);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool HasSource() const { return valid_values_mask_ & HAS_SOURCE; }
|
||||
Location &SetSource(Source source) {
|
||||
source_ = source;
|
||||
valid_values_mask_ = static_cast<Mask>(valid_values_mask_ | HAS_SOURCE);
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool HasAltitude() const { return valid_values_mask_ & HAS_ALTITUDE; }
|
||||
Location &SetAltitude(double altitude_m, double vertical_accuracy_m) {
|
||||
if (vertical_accuracy_m > 0.0) {
|
||||
altitude_m_ = altitude_m;
|
||||
vertical_accuracy_m_ = vertical_accuracy_m;
|
||||
valid_values_mask_ = static_cast<Mask>(valid_values_mask_ | HAS_ALTITUDE);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool HasBearing() const { return valid_values_mask_ & HAS_BEARING; }
|
||||
Location &SetBearing(double bearing_deg) {
|
||||
if (bearing_deg >= 0.0) {
|
||||
bearing_deg_ = bearing_deg;
|
||||
valid_values_mask_ = static_cast<Mask>(valid_values_mask_ | HAS_BEARING);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool HasSpeed() const { return valid_values_mask_ & HAS_SPEED; }
|
||||
Location &SetSpeed(double speed_mps) {
|
||||
if (speed_mps >= 0.0) {
|
||||
speed_mps_ = speed_mps;
|
||||
valid_values_mask_ = static_cast<Mask>(valid_values_mask_ | HAS_SPEED);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void save(Archive &ar) const {
|
||||
ar(Encode());
|
||||
}
|
||||
|
||||
template <class Archive>
|
||||
void load(Archive &ar) {
|
||||
std::string encoded_location;
|
||||
ar(encoded_location);
|
||||
Decode(encoded_location);
|
||||
}
|
||||
|
||||
std::string ToDebugString() const {
|
||||
std::ostringstream stream;
|
||||
stream << '<' << std::fixed;
|
||||
if (valid_values_mask_ & HAS_LATLON) {
|
||||
stream << "utc=" << timestamp_ms_ << ",lat=" << std::setprecision(7) << latitude_deg_
|
||||
<< ",lon=" << std::setprecision(7) << longitude_deg_ << ",acc=" << std::setprecision(2)
|
||||
<< horizontal_accuracy_m_;
|
||||
}
|
||||
if (valid_values_mask_ & HAS_ALTITUDE) {
|
||||
stream << ",alt=" << std::setprecision(2) << altitude_m_ << ",vac=" << std::setprecision(2)
|
||||
<< vertical_accuracy_m_;
|
||||
}
|
||||
if (valid_values_mask_ & HAS_BEARING) {
|
||||
stream << ",bea=" << std::setprecision(7) << bearing_deg_;
|
||||
}
|
||||
if (valid_values_mask_ & HAS_SPEED) {
|
||||
stream << ",spd=" << std::setprecision(2) << speed_mps_;
|
||||
}
|
||||
if (valid_values_mask_ & HAS_SOURCE) {
|
||||
stream << ",src=";
|
||||
switch (source_) {
|
||||
case Source::GPS:
|
||||
stream << "GPS";
|
||||
break;
|
||||
case Source::NETWORK:
|
||||
stream << "Net";
|
||||
break;
|
||||
case Source::PASSIVE:
|
||||
stream << "Psv";
|
||||
break;
|
||||
default:
|
||||
stream << "Unk";
|
||||
break;
|
||||
}
|
||||
}
|
||||
stream << '>';
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename T>
|
||||
static inline void AppendToStringAsBinary(std::string &str, const T &value, size_t bytes = sizeof(T)) {
|
||||
str.append(reinterpret_cast<const char *>(&value), bytes);
|
||||
}
|
||||
};
|
||||
} // namespace alohalytics
|
||||
|
||||
#endif // LOCATION_H
|
|
@ -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
|
||||
|
@ -53,7 +53,6 @@ class Logger {
|
|||
Logger(const char* file, int line) { out_ << file << ':' << line << ": "; }
|
||||
|
||||
~Logger() {
|
||||
// out_ << std::endl;
|
||||
#if defined(__OBJC__)
|
||||
NSLog(@"Alohalytics: %s", out_.str().c_str());
|
||||
#elif defined(ANDROID)
|
||||
|
@ -65,7 +64,8 @@ class Logger {
|
|||
|
||||
template <typename T, typename... ARGS>
|
||||
void Log(const T& arg1, const ARGS&... others) {
|
||||
out_ << arg1 << ' ';
|
||||
Log(arg1);
|
||||
out_ << ' ';
|
||||
Log(others...);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
#include <stdio.h> // popen
|
||||
#include <fstream>
|
||||
#include <iostream> // std::cerr
|
||||
#include <iostream> // std::cerr
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define popen _popen
|
||||
|
|
18
3party/Alohalytics/tests/test_gzip.cc
Normal file
18
3party/Alohalytics/tests/test_gzip.cc
Normal file
|
@ -0,0 +1,18 @@
|
|||
#include "../src/gzip_wrapper.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
int main(int, char **) {
|
||||
std::string data;
|
||||
for (int i = 0; i < 10000 + rand(); ++i) {
|
||||
data.push_back(rand());
|
||||
}
|
||||
const std::string gzipped = alohalytics::Gzip(data);
|
||||
const std::string ungzipped = alohalytics::Gunzip(gzipped);
|
||||
if (ungzipped != data) {
|
||||
std::cerr << "Gzip/gunzip test has failed" << std::endl;
|
||||
return -1;
|
||||
}
|
||||
std::cout << "Test has passed" << std::endl;
|
||||
return 0;
|
||||
}
|
88
3party/Alohalytics/tests/test_location.cc
Normal file
88
3party/Alohalytics/tests/test_location.cc
Normal file
|
@ -0,0 +1,88 @@
|
|||
/*******************************************************************************
|
||||
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.
|
||||
*******************************************************************************/
|
||||
#include "../src/location.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <math.h>
|
||||
|
||||
using alohalytics::Location;
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
|
||||
#define ASSERT_EQUAL(x, y) \
|
||||
if ((x) != (y)) { \
|
||||
cerr << "Test failed: " << #x << " != " << #y << " (" << (x) << " != " << (y) << ")" << endl; \
|
||||
return -1; \
|
||||
}
|
||||
#define ASSERT_ALMOST_EQUAL(x, y, epsilon) \
|
||||
if (fabs((x) - (y)) > epsilon) { \
|
||||
cerr << "Test failed: " << #x << " ~!= " << #y << " (" << (x) << " ~!= " << (y) << ")" << endl; \
|
||||
return -1; \
|
||||
}
|
||||
|
||||
int main(int, char **) {
|
||||
cerr.precision(20);
|
||||
cerr << std::fixed;
|
||||
|
||||
const uint64_t timestamp = 1423169916587UL;
|
||||
const double lat = -84.123456789;
|
||||
const double lon = 177.123456789;
|
||||
const double horizontal_accuracy = 0.345678912345;
|
||||
const double alt = -11.123456789;
|
||||
const double vertical_accuracy = 0.987654321;
|
||||
const double speed = 27.123456789;
|
||||
const double bearing = 0.123456789;
|
||||
const Location::Source source = alohalytics::Location::Source::NETWORK;
|
||||
|
||||
Location l0;
|
||||
const std::string serialized = l0.SetLatLon(timestamp, lat, lon, horizontal_accuracy)
|
||||
.SetAltitude(alt, vertical_accuracy)
|
||||
.SetSpeed(speed)
|
||||
.SetBearing(bearing)
|
||||
.SetSource(source)
|
||||
.Encode();
|
||||
ASSERT_EQUAL(serialized.size(), 32);
|
||||
|
||||
const alohalytics::Location l(serialized);
|
||||
ASSERT_EQUAL(l.HasLatLon(), true);
|
||||
ASSERT_EQUAL(l.timestamp_ms_, timestamp);
|
||||
ASSERT_ALMOST_EQUAL(l.latitude_deg_, lat, 1e-7);
|
||||
ASSERT_ALMOST_EQUAL(l.longitude_deg_, lon, 1e-7);
|
||||
ASSERT_ALMOST_EQUAL(l.horizontal_accuracy_m_, horizontal_accuracy, 1e-2);
|
||||
ASSERT_EQUAL(l.HasAltitude(), true);
|
||||
ASSERT_ALMOST_EQUAL(l.altitude_m_, alt, 1e-2);
|
||||
ASSERT_ALMOST_EQUAL(l.vertical_accuracy_m_, vertical_accuracy, 1e-2);
|
||||
ASSERT_EQUAL(l.HasBearing(), true);
|
||||
ASSERT_ALMOST_EQUAL(l.bearing_deg_, bearing, 1e-7);
|
||||
ASSERT_EQUAL(l.HasSpeed(), true);
|
||||
ASSERT_ALMOST_EQUAL(l.speed_mps_, speed, 1e-2);
|
||||
ASSERT_EQUAL(l.HasSource(), true);
|
||||
ASSERT_EQUAL(l.source_, source);
|
||||
|
||||
std::cout << "All tests have passed." << endl;
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Reference in a new issue