package org.commcare.google.services.analytics;
import android.os.Build;
import android.preference.Preference;
import android.preference.PreferenceManager;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Tracker;
import org.commcare.AppUtils;
import org.commcare.CommCareApplication;
import org.commcare.activities.CommCareSetupActivity;
import org.commcare.android.logging.ReportingUtils;
import org.commcare.dalvik.BuildConfig;
import org.commcare.preferences.CommCarePreferences;
import org.commcare.utils.EncryptionUtils;
import java.util.Map;
/**
* All methods used to report events to google analytics, and all supporting utils
*
* @author amstone
*/
public class GoogleAnalyticsUtils {
/**
* Report a google analytics event that has only a category and an action
*/
private static void reportEvent(String category, String action) {
if (analyticsDisabled() || versionIncompatible()) {
return;
}
getTracker().send(new HitBuilders.EventBuilder()
.setCustomDimension(1, CommCareApplication.instance().getCurrentUserId())
.setCustomDimension(2, ReportingUtils.getDomain())
.setCustomDimension(3, BuildConfig.FLAVOR)
.setCustomDimension(4, "" + CommCareApplication.instance().isConsumerApp())
.setCustomDimension(5, ReportingUtils.getAppId())
.setCategory(category)
.setAction(action)
.build());
}
/**
* Report a google analytics event that has a category, action, and label
*/
private static void reportEvent(String category, String action, String label) {
if (analyticsDisabled() || versionIncompatible()) {
return;
}
getTracker().send(new HitBuilders.EventBuilder()
.setCustomDimension(1, CommCareApplication.instance().getCurrentUserId())
.setCustomDimension(2, ReportingUtils.getDomain())
.setCustomDimension(3, BuildConfig.FLAVOR)
.setCustomDimension(4, "" + CommCareApplication.instance().isConsumerApp())
.setCustomDimension(5, ReportingUtils.getAppId())
.setCategory(category)
.setAction(action)
.setLabel(label)
.build());
}
/**
* Report a google analytics event that has a category, action, label, and value
*/
private static void reportEvent(String category, String action, String label, int value) {
if (analyticsDisabled() || versionIncompatible()) {
return;
}
getTracker().send(new HitBuilders.EventBuilder()
.setCustomDimension(1, CommCareApplication.instance().getCurrentUserId())
.setCustomDimension(2, ReportingUtils.getDomain())
.setCustomDimension(3, BuildConfig.FLAVOR)
.setCustomDimension(4, "" + CommCareApplication.instance().isConsumerApp())
.setCustomDimension(5, ReportingUtils.getAppId())
.setCategory(category)
.setAction(action)
.setLabel(label)
.setValue(value)
.build());
}
public static void reportAudioFileChosen(){
reportEvent(GoogleAnalyticsFields.CATEGORY_AUDIO_WIDGET,
GoogleAnalyticsFields.ACTION_CHOOSE_FILE);
}
public static void reportRecordingPopupOpened(){
reportEvent(GoogleAnalyticsFields.CATEGORY_AUDIO_WIDGET,
GoogleAnalyticsFields.ACTION_START_RECORDING_DIALOG);
}
public static void reportAudioPlayed(){
reportEvent(GoogleAnalyticsFields.CATEGORY_AUDIO_WIDGET,
GoogleAnalyticsFields.ACTION_PLAY_AUDIO);
}
public static void reportAudioPaused(){
reportEvent(GoogleAnalyticsFields.CATEGORY_AUDIO_WIDGET,
GoogleAnalyticsFields.ACTION_PAUSE_AUDIO);
}
public static void reportAudioFileSaved(){
reportEvent(GoogleAnalyticsFields.CATEGORY_AUDIO_WIDGET,
GoogleAnalyticsFields.ACTION_SAVE_RECORDING);
}
public static void reportRecordingStarted(){
reportEvent(GoogleAnalyticsFields.CATEGORY_AUDIO_WIDGET,
GoogleAnalyticsFields.ACTION_START_RECORD);
}
public static void reportRecordingStopped(){
reportEvent(GoogleAnalyticsFields.CATEGORY_AUDIO_WIDGET,
GoogleAnalyticsFields.ACTION_STOP_RECORD);
}
public static void reportRecordingRecycled(){
reportEvent(GoogleAnalyticsFields.CATEGORY_AUDIO_WIDGET,
GoogleAnalyticsFields.ACTION_RECORD_AGAIN);
}
public static void reportGraphViewAttached(){
reportEvent(GoogleAnalyticsFields.CATEGORY_GRAPHING,
GoogleAnalyticsFields.ACTION_GRAPH_ATTACH);
}
public static void reportGraphViewDetached(){
reportEvent(GoogleAnalyticsFields.CATEGORY_GRAPHING,
GoogleAnalyticsFields.ACTION_GRAPH_DETACH);
}
public static void reportGraphViewFullScreenOpened(){
reportEvent(GoogleAnalyticsFields.CATEGORY_GRAPHING,
GoogleAnalyticsFields.ACTION_GRAPH_FULLSCREEN_OPEN);
}
public static void reportGraphViewFullScreenClosed(){
reportEvent(GoogleAnalyticsFields.CATEGORY_GRAPHING,
GoogleAnalyticsFields.ACTION_GRAPH_FULLSCREEN_CLOSE);
}
/**
* Report a user event of navigating forward in form entry
*
* @param label - Communicates the user's method of navigation (swipe vs. arrow press)
* @param value - Communicates if form was in completed state when navigation occurred
*/
public static void reportFormNavForward(String label, int value) {
reportEvent(
GoogleAnalyticsFields.CATEGORY_FORM_ENTRY,
GoogleAnalyticsFields.ACTION_FORWARD,
label, value);
}
/**
* Report a user event of navigating backward in form entry
*
* @param label - Communicates the user's method of navigation (swipe vs. arrow press)
*/
public static void reportFormNavBackward(String label) {
reportEvent(
GoogleAnalyticsFields.CATEGORY_FORM_ENTRY,
GoogleAnalyticsFields.ACTION_BACKWARD,
label);
}
/**
* Report a user event of triggering a form exit attempt, and which mode they used to do so
*
* @param label - Indicates the way in which the user triggered the form exit
*/
public static void reportFormQuitAttempt(String label) {
reportEvent(GoogleAnalyticsFields.CATEGORY_FORM_ENTRY,
GoogleAnalyticsFields.ACTION_TRIGGER_QUIT_ATTEMPT, label);
}
/**
* Report an event of a form being exited
*
* @param label - Communicates which option the user selected on the exit form dialog, or none
* if form exit occurred without showing the dialog at all
*/
public static void reportFormExit(String label) {
reportEvent(GoogleAnalyticsFields.CATEGORY_FORM_ENTRY,
GoogleAnalyticsFields.ACTION_EXIT_FORM, label);
}
public static void reportHomeButtonClick(String buttonLabel) {
reportEvent(GoogleAnalyticsFields.CATEGORY_HOME_SCREEN,
GoogleAnalyticsFields.ACTION_BUTTON,
buttonLabel);
}
/**
* Report a user event of opening an options menu
*/
public static void reportOptionsMenuEntry(String category) {
reportEvent(category, GoogleAnalyticsFields.ACTION_OPTIONS_MENU);
}
/**
* Report a user event of selecting an item within an options menu
*/
public static void reportOptionsMenuItemEntry(String category, String label) {
reportEvent(category, GoogleAnalyticsFields.ACTION_OPTIONS_MENU_ITEM, label);
}
/**
* Report a user event of opening a preferences menu
*/
public static void reportPrefActivityEntry(String category) {
reportEvent(category, GoogleAnalyticsFields.ACTION_PREF_MENU);
}
/**
* Report a user event of opening the edit dialog for an item in a preferences menu
*/
public static void reportPrefItemClick(String category, String label) {
reportEvent(category, GoogleAnalyticsFields.ACTION_VIEW_PREF, label);
}
public static void reportAdvancedActionItemClick(String action) {
reportEvent(GoogleAnalyticsFields.CATEGORY_ADVANCED_ACTIONS, action);
}
/**
* Report a user event of changing the value of an item in a preferences menu
*/
public static void reportEditPref(String category, String label, int value) {
if (analyticsDisabled() || versionIncompatible()) {
return;
}
HitBuilders.EventBuilder builder = new HitBuilders.EventBuilder();
builder.setCategory(category)
.setCustomDimension(1, CommCareApplication.instance().getCurrentUserId())
.setCustomDimension(2, ReportingUtils.getDomain())
.setCustomDimension(3, BuildConfig.FLAVOR)
.setAction(GoogleAnalyticsFields.ACTION_EDIT_PREF)
.setLabel(label);
if (value != -1) {
builder.setValue(value);
}
getTracker().send(builder.build());
}
public static void reportEditPref(String category, String label) {
reportEditPref(category, label, -1);
}
/**
* Report an event of an attempted sync
*
* @param action - Communicates whether the sync was user-triggered or auto-triggered
* @param label - Communicates if the sync was successful
* @param value - Communicates the nature of the sync if it was successful,
* OR the reason for failure if the sync was unsuccessful
*/
public static void reportSyncAttempt(String action, String label, int value) {
reportEvent(GoogleAnalyticsFields.CATEGORY_SERVER_COMMUNICATION, action, label, value);
}
/**
* Report a user event of viewing a list of archived forms
*
* @param label - Communicates whether the user is viewing incomplete forms or saved forms
*/
public static void reportViewArchivedFormsList(String label) {
reportEvent(GoogleAnalyticsFields.CATEGORY_ARCHIVED_FORMS,
GoogleAnalyticsFields.ACTION_VIEW_FORMS_LIST, label);
}
/**
* Report a user event of opening up a form from a list of archived forms
*
* @param label - Communicates whether the form was from the list of incomplete or saved forms
*/
public static void reportOpenArchivedForm(String label) {
reportEvent(GoogleAnalyticsFields.CATEGORY_ARCHIVED_FORMS,
GoogleAnalyticsFields.ACTION_OPEN_ARCHIVED_FORM, label);
}
public static void reportAppInstall(int lastInstallModeCode) {
reportEvent(GoogleAnalyticsFields.CATEGORY_APP_INSTALL,
CommCareSetupActivity.getAnalyticsActionFromInstallMode(lastInstallModeCode),
AppUtils.getCurrentVersionString());
}
/**
* Report a user event of navigating backward out of the entity detail screen
*
* @param isSwipe - Toggles user's method of navigation to swipe or arrow press
*/
public static void reportEntityDetailExit(boolean isSwipe, boolean isSingleTab) {
reportEntityDetailNavigation(
GoogleAnalyticsFields.ACTION_EXIT_FROM_DETAIL, isSwipe, isSingleTab);
}
/**
* Report a user event of continuing forward out of the entity detail screen
*
* @param isSwipe - Toggles user's method of navigation to swipe or arrow press
*/
public static void reportEntityDetailContinue(boolean isSwipe, boolean isSingleTab) {
reportEntityDetailNavigation(
GoogleAnalyticsFields.ACTION_CONTINUE_FROM_DETAIL, isSwipe, isSingleTab);
}
private static void reportEntityDetailNavigation(String action, boolean isSwipe, boolean isSingleTab) {
reportEvent(
GoogleAnalyticsFields.CATEGORY_MODULE_NAVIGATION,
action,
isSwipe ? GoogleAnalyticsFields.LABEL_SWIPE : GoogleAnalyticsFields.LABEL_ARROW,
isSingleTab ? GoogleAnalyticsFields.VALUE_DOESNT_HAVE_TABS : GoogleAnalyticsFields.VALUE_HAS_TABS);
}
/**
* Report usage of a specific feature
*
* @param action - Should be one of the actions listed under
* "Actions for CATEGORY_FEATURE_USAGE" in GoogleAnalyticsFields.java
*/
public static void reportFeatureUsage(String action) {
reportEvent(GoogleAnalyticsFields.CATEGORY_FEATURE_USAGE, action);
}
/**
* Report an action in the app manager
*
* @param action - Should be one of the actions listed under
* "Actions for CATEGORY_APP_MANAGER" in GoogleAnalyticsFields.java
*/
public static void reportAppManagerAction(String action) {
reportEvent(GoogleAnalyticsFields.CATEGORY_APP_MANAGER, action);
}
public static void reportPrivilegeEnabled(String privilegeName, String username) {
reportEvent(GoogleAnalyticsFields.CATEGORY_PRIVILEGE_ENABLED, privilegeName,
EncryptionUtils.getMD5HashAsString(username));
}
public static void reportLanguageAtPointOfFormEntry(String language) {
reportEvent(GoogleAnalyticsFields.CATEGORY_LANGUAGE_STATS,
GoogleAnalyticsFields.ACTION_LANGUAGE_AT_FORM_ENTRY, language);
}
public static void reportAndroidApiLevelAtStartup() {
reportEvent(GoogleAnalyticsFields.CATEGORY_HIGH_LEVEL_STATS,
GoogleAnalyticsFields.ACTION_ANDROID_API_LEVEL_AT_STARTUP,
"" + Build.VERSION.SDK_INT);
}
/**
* Report the length of a certain user event/action/concept
*
* @param action - Communicates the event/action/concept whose length is being measured
* @param value - Communicates the duration, in seconds
*/
public static void reportTimedEvent(String action, int value) {
if (analyticsDisabled() || versionIncompatible()) {
return;
}
getTracker().send(new HitBuilders.EventBuilder()
.setCustomDimension(1, CommCareApplication.instance().getCurrentUserId())
.setCustomDimension(2, ReportingUtils.getDomain())
.setCustomDimension(3, BuildConfig.FLAVOR)
.setCategory(GoogleAnalyticsFields.CATEGORY_TIMED_EVENTS)
.setAction(action)
.setValue(value)
.build());
}
public static void createPreferenceOnClickListeners(PreferenceManager prefManager,
Map<String, String> menuIdToAnalyticsEvent, String category) {
for (String prefKey : menuIdToAnalyticsEvent.keySet()) {
createPreferenceOnClickListener(prefManager, prefKey, category,
menuIdToAnalyticsEvent.get(prefKey));
}
}
public static void createPreferenceOnClickListener(PreferenceManager manager,
String prefKey,
final String category,
final String analyticsLabel) {
Preference pref = manager.findPreference(prefKey);
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
GoogleAnalyticsUtils.reportPrefItemClick(category, analyticsLabel);
return true;
}
});
}
private static Tracker getTracker() {
return CommCareApplication.instance().getDefaultTracker();
}
private static boolean analyticsDisabled() {
return !CommCarePreferences.isAnalyticsEnabled();
}
public static boolean versionIncompatible() {
return Build.VERSION.SDK_INT < Build.VERSION_CODES.GINGERBREAD;
}
}