package kr.kdev.dg1s.biowiki.util;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.mixpanel.android.mpmetrics.MixpanelAPI;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.HashMap;
import kr.kdev.dg1s.biowiki.BioWiki;
import kr.kdev.dg1s.biowiki.Config;
import kr.kdev.dg1s.biowiki.Constants;
/**
* Created by Eric on 11/20/13.
* <p/>
* WPMobileStatsUtils handles stats logging. Its public methods should for the most part be service
* neutral to allow to ease possible changes in the stats libs we use in the future.
* <p/>
* WPMobileStatsUtils is not thread safe so only call it from the main thread.
*/
public class BWMobileStatsUtil {
/* Events */
public static final String StatsEventAppOpened = "Application Opened";
// Posts
public static final String StatsEventPostsOpened = "Posts - Opened";
public static final String StatsEventPostsClosed = "Posts - Closed";
public static final String StatsEventPostsClickedNewPost = "Posts - Clicked New Post";
// Post Detail
public static final String StatsPropertyPostMenuClickedEdit = "menu_clicked_edit";
public static final String StatsPropertyPostMenuClickedComment = "menu_clicked_comment";
public static final String StatsPropertyPostMenuClickedShare = "menu_clicked_share";
public static final String StatsPropertyPostMenuClickedPreview = "menu_clicked_preview";
public static final String StatsPropertyPostMenuClickedDelete = "menu_clicked_delete";
public static final String StatsPropertyPostDetailClickedEdit = "clicked_edit";
public static final String StatsPropertyPostDetailClickedComment = "clicked_comment";
public static final String StatsPropertyPostDetailClickedShare = "clicked_share";
public static final String StatsPropertyPostDetailClickedPreview = "clicked_preview";
public static final String StatsPropertyPostDetailClickedDelete = "clicked_delete";
public static final String StatsPropertyPostDetailClickedShowCategories = "clicked_show_categories";
public static final String StatsPropertyPostDetailClickedKeyboardToolbarBoldButton = "clicked_keyboard_toolbar_bold_button";
public static final String StatsPropertyPostDetailClickedKeyboardToolbarItalicButton = "clicked_keyboard_toolbar_italic_button";
public static final String StatsPropertyPostDetailClickedKeyboardToolbarUnderlineButton = "clicked_keyboard_toolbar_underline_button";
public static final String StatsPropertyPostDetailClickedKeyboardToolbarLinkButton = "clicked_keyboard_toolbar_link_button";
public static final String StatsPropertyPostDetailClickedKeyboardToolbarBlockquoteButton = "clicked_keyboard_toolbar_blockquote_button";
public static final String StatsPropertyPostDetailClickedKeyboardToolbarDelButton = "clicked_keyboard_toolbar_del_button";
public static final String StatsPropertyPostDetailClickedKeyboardToolbarMoreButton = "clicked_keyboard_toolbar_more_button";
public static final String StatsPropertyPostDetailClickedKeyboardToolbarPictureButton = "clicked_keyboard_toolbar_picture_button";
public static final String StatsPropertyPostDetailClickedUpdate = "clicked_update_button";
public static final String StatsPropertyPostDetailClickedPublish = "clicked_publish_button";
public static final String StatsEventPostDetailOpenedEditor = "Post - Opened Editor";
public static final String StatsEventPostDetailClosedEditor = "Post - Closed Editor";
// Post Detail - Settings
public static final String StatsPropertyPostDetailSettingsClickedStatus = "settings_clicked_status";
public static final String StatsPropertyPostDetailSettingsClickedScheduleFor = "settings_clicked_schedule_for";
public static final String StatsPropertyPostDetailSettingsClickedPostFormat = "settings_clicked_post_format";
public static final String StatsPropertyPostDetailSettingsClickedAddLocation = "settings_clicked_add_location";
public static final String StatsPropertyPostDetailSettingsClickedUpdateLocation = "settings_clicked_update_location";
public static final String StatsPropertyPostDetailSettingsClickedRemoveLocation = "settings_clicked_remove_location";
// Pages
public static final String StatsPropertyPagesOpened = "pages_opened";
public static final String StatsEventPagesOpened = "Pages - Opened";
public static final String StatsEventPagesClosed = "Pages - Closed";
public static final String StatsEventPagesClickedNewPage = "Pages - Clicked New Page";
public static final String StatsEventPageDetailOpenedEditor = "Page - Opened Editor";
public static final String StatsEventPageDetailClosedEditor = "Page - Closed Editor";
// Exception logging
public static final String StatsEventException = "Exception";
public static final String StatsPropertyExceptionNoteParsing = "note_html_parsing_failed";
public static final String StatsPropertyExceptionFetchMedia = "fetch_media_failed";
public static final String StatsPropertyExceptionUploadMedia = "upload_media_failed";
// Track Out of Memory errors
public static final String StatsEventMediaOutOfMemory = "Out of Memory Error";
/* /Events */
private static final BWMobileStatsUtil instance = new BWMobileStatsUtil();
private static MixpanelAPI mixpanel;
private HashMap<String, JSONObject> aggregatedProperties;
private BWMobileStatsUtil() {
aggregatedProperties = new HashMap<String, JSONObject>();
}
/*
Singleton
*/
public static BWMobileStatsUtil getInstance() {
return instance;
}
/*
Sets up and configures some session wide tracking.
Should be called when the application is launched and at sign in/out.
*/
public static void initialize() {
mixpanel = MixpanelAPI.getInstance(BioWiki.getContext(), Config.MIXPANEL_TOKEN);
// Tracking session count will help us isolate users who just installed the app
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(BioWiki.getContext());
int sessionCount = preferences.getInt("sessionCount", 0);
sessionCount++;
SharedPreferences.Editor editor = preferences.edit();
editor.putInt("sessionCount", sessionCount);
editor.commit();
// Register super properties
boolean connected = /*BioWiki.hasValidWPComCredentials(BioWiki.getContext())*/ false;
int numBlogs = BioWiki.wpDB.getVisibleAccounts().size();
try {
JSONObject properties = new JSONObject();
properties.put("platform", "Android");
properties.put("session_count", sessionCount);
properties.put("connected_to_dotcom", connected);
properties.put("number_of_blogs", numBlogs);
mixpanel.registerSuperProperties(properties);
} catch (JSONException e) {
AppLog.e(AppLog.T.UTILS, e);
}
// Application opened and start.
if (connected) {
String username = preferences.getString(BioWiki.WPCOM_USERNAME_PREFERENCE, null);
mixpanel.identify(username);
mixpanel.getPeople().increment("Application Opened", 1);
try {
JSONObject jsonObj = new JSONObject();
jsonObj.put("$username", username);
jsonObj.put("$first_name", username);
mixpanel.getPeople().set(jsonObj);
} catch (JSONException e) {
AppLog.e(AppLog.T.UTILS, e);
}
}
}
// WPCom Stats
/*
Bump WordPress.com stats.
@param statName The name of the stat to bump.
*/
public static void pingWPComStats(String statName) {
Response.ErrorListener errorListener = new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
String errMsg = String.format("Error pinging WPCom Stats: %s", volleyError.getMessage());
AppLog.w(AppLog.T.STATS, errMsg);
}
};
int rnd = (int) (Math.random() * 100000);
String statsURL = String.format("%s%s%s%s%d", Constants.readerURL_v3, "&template=stats&stats_name=", statName, "&rnd=", rnd);
StringRequest req = new StringRequest(Request.Method.GET, statsURL, null, errorListener);
BioWiki.requestQueue.add(req);
}
// Generic
/*
Track an event for WordPress self-hosted and .com users.
@param event One of the constants representing the event to track.
*/
public static void trackEventForSelfHostedAndWPCom(String event) {
BWMobileStatsUtil.instance.track(false, event);
}
/*
Track an event for WordPress self-hosted and .com users.
@param event One of the constants representing the event to track.
@param properties JSONObject of keys/values to track. Valid properties are boolean, integer and strings.
*/
public static void trackEventForSelfHostedAndWPCom(String event, JSONObject properties) {
BWMobileStatsUtil.instance.track(false, event, properties);
}
/*
Track an event for WordPress self-hosted and .com users.
@param event One of the constants representing the event to track.
*/
public static void trackEventForSelfHostedAndWPComWithSavedProperties(String event) {
BWMobileStatsUtil.instance.trackWithSavedProperties(false, event);
}
/*
Track an event WordPress only for .com users.
@param event One of the constants representing the event to track.
*/
public static void trackEventForWPCom(String event) {
BWMobileStatsUtil.instance.track(true, event);
}
/*
Track an event WordPress only for .com users.
@param event One of the constants representing the event to track.
@param properties JSONObject of keys/values to track. Valid properties are boolean, integer and strings.
*/
public static void trackEventForWPCom(String event, JSONObject properties) {
BWMobileStatsUtil.instance.track(true, event, properties);
}
/*
Track an event WordPress only for .com users.
@param event One of the constants representing the event to track.
*/
public static void trackEventForWPComWithSavedProperties(String event) {
BWMobileStatsUtil.instance.trackWithSavedProperties(true, event);
}
/*
Clears saved properties for the current session.
*/
public static void clearPropertiesForAllEvents() {
BWMobileStatsUtil.instance.clearProperties();
}
/*
Increments the specified property. If the property does not exist it is created.
A subsequent call to trackEventWithSavedProperties or trackEventForWPComWithSavedProperties
should be made to record the property with the stats service.
@param event One of the constants representing the event to track.
@param property The property to increment.
*/
public static void incrementProperty(String event, String property) {
BWMobileStatsUtil.instance.increment(event, property);
}
/*
Flags the specified property. If the property does not exist it is created.
A subsequent call to trackEventWithSavedProperties or trackEventForWPComWithSavedProperties
should be made to record the property with the stats service.
@param event One of the constants representing the event to track.
@param property The property to flag.
*/
public static void flagProperty(String event, String property) {
BWMobileStatsUtil.instance.flag(event, property);
}
/**
* Tracks the exception stack trace associated to a String id
*
* @param throwable contains stack trace
* @param exceptionId a string id (short name that helps to distinguish different tracked
* exception)
*/
public static void trackException(Throwable throwable, String exceptionId) {
trackException(throwable, exceptionId, null);
}
/**
* Tracks the exception stack trace associated to a String id
* <p/>
* param exception exception we want to track - contains stack trace
*
* @param exceptionId a string id (short name that helps to distinguish different tracked
* exception)
* @param additionalData a JSON Object to track additional data that could help solve this
* exception
*/
public static void trackException(Throwable throwable, String exceptionId,
JSONObject additionalData) {
JSONObject properties = new JSONObject();
try {
properties.put("exception_id", exceptionId);
if (throwable != null) {
StringWriter errors = new StringWriter();
throwable.printStackTrace(new PrintWriter(errors));
properties.put("stacktrace", errors.toString());
}
if (additionalData != null) {
properties.put("additional_data", additionalData);
}
} catch (JSONException jsonException) {
AppLog.e(AppLog.T.UTILS, jsonException);
}
mixpanel.track(StatsEventException, properties);
}
// Instance methods
private boolean connectedToWPCom() {
return /*BioWiki.hasValidWPComCredentials(BioWiki.getContext()) */ false;
}
private void track(boolean isWPCom, String event) {
if (isWPCom && !connectedToWPCom()) {
return;
}
mixpanel.track(event, null);
}
private void track(boolean isWPCom, String event, JSONObject properties) {
if (isWPCom && !connectedToWPCom()) {
return;
}
mixpanel.track(event, properties);
}
private void trackWithSavedProperties(boolean isWPCom, String event) {
if (isWPCom && !connectedToWPCom()) {
return;
}
JSONObject properties = aggregatedProperties.get(event);
if (properties == null) {
properties = new JSONObject();
aggregatedProperties.put(event, properties);
}
mixpanel.track(event, properties);
mixpanel.flush();
}
private void clearProperties() {
aggregatedProperties.clear();
}
/*
Increments the specified property, for the specified event.
@param event One of the constants representing the event to track.
@param property The name of the property.
*/
private void increment(String event, String property) {
Integer count = (Integer) propertyForEvent(event, property);
count++;
saveProperty(event, property, count);
}
/*
Flags the specified property as true, for the specified event.
@param event One of the constants representing the event to track.
@param property The name of the property.
*/
private void flag(String event, String property) {
saveProperty(event, property, true);
}
/*
Returns the value of the specified property for the specified event.
If the property does not exist 0 is returned.
Return values are a boxed primitive of type boolean, integer, or string.
@param event One of the constants representing the event to track.
@param property The name of the property.
*/
private Object propertyForEvent(String event, String property) {
JSONObject properties = aggregatedProperties.get(event);
Object val = 0;
try {
val = properties.get(property);
} catch (JSONException e) {
// The key didn't exist. No worries, use the default of 0.
}
return val;
}
/*
Save locally the specified property and value for the specified event.
@param event One of the constants representing the event to track.
@param property The name of the property.
@param value A boxed primitive of type boolean, integer or string.
*/
private void saveProperty(String event, String property, Object value) {
JSONObject properties = aggregatedProperties.get(event);
if (properties == null) {
properties = new JSONObject();
aggregatedProperties.put(event, properties);
}
try {
properties.put(property, value);
} catch (JSONException e) {
AppLog.e(AppLog.T.UTILS, e);
}
}
private JSONObject propertiesForEvent(String event) {
return aggregatedProperties.get(event);
}
}