package bf.io.openshop.utils;
import android.content.Context;
import android.os.Bundle;
import com.facebook.appevents.AppEventsConstants;
import com.facebook.appevents.AppEventsLogger;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Tracker;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import bf.io.openshop.MyApplication;
import bf.io.openshop.R;
import bf.io.openshop.SettingsMy;
import bf.io.openshop.entities.Shop;
import bf.io.openshop.entities.cart.Cart;
import bf.io.openshop.entities.cart.CartProductItem;
import bf.io.openshop.entities.delivery.Shipping;
import timber.log.Timber;
public class Analytics {
private static final String TRACKER_GLOBAL = "Global";
private static final String TRACKER_APP = "App";
public static final String PRODUCT = "product";
public static final String POST_ORDER = "POST_ORDER";
private static HashMap<String, Tracker> mTrackers = new HashMap<>();
private static AppEventsLogger facebookLogger;
private static String campaignUri;
private Analytics() {}
/**
* Prepare Google analytics trackers and Facebook events logger.
* Send UTM campaign if exist.
*
* @param shop shop with app specific Google Ua or null, if global tracker is enough.
* @param context application context.
*/
public static synchronized void prepareTrackersAndFbLogger(Shop shop, Context context) {
GoogleAnalytics analytics = GoogleAnalytics.getInstance(context);
// To enable debug logging use: adb shell setprop log.tag.GAv4 DEBUG
if (shop == null) {
deleteAppTrackers();
} else {
if (!mTrackers.containsKey(TRACKER_APP) && analytics != null) {
if (shop.getGoogleUa() != null && !shop.getGoogleUa().isEmpty()) {
Timber.d("Set new app tracker with id: %s", shop.getGoogleUa());
// App tracker determined by shop
Tracker appTracker = analytics.newTracker(shop.getGoogleUa());
appTracker.enableAutoActivityTracking(true);
appTracker.enableExceptionReporting(false);
appTracker.enableAdvertisingIdCollection(true);
mTrackers.put(TRACKER_APP, appTracker);
} else {
Timber.e(new RuntimeException(), "Creating GA app tracker with empty Google UA");
}
} else {
Timber.e("Trackers for this app already exist.");
}
}
// Add global tracker only one time.
if (!mTrackers.containsKey(TRACKER_GLOBAL) && analytics != null) {
Timber.d("Set new global tracker.");
// Global app tracker
Tracker appTrackerGlobal = analytics.newTracker(R.xml.global_tracker);
appTrackerGlobal.enableAutoActivityTracking(true);
appTrackerGlobal.enableExceptionReporting(true);
appTrackerGlobal.enableAdvertisingIdCollection(true);
mTrackers.put(TRACKER_GLOBAL, appTrackerGlobal);
// Send camping info only once time.
sendCampaignInfo();
}
facebookLogger = AppEventsLogger.newLogger(MyApplication.getInstance());
}
/**
* @return content of campaignUri private field.
*/
public static String getCampaignUri(){
return campaignUri;
}
/**
* Method delete shop specific tracker if exist.
*/
public static void deleteAppTrackers() {
if (mTrackers != null && mTrackers.containsKey(TRACKER_APP)) {
Timber.d("Removing GA app tracker.");
mTrackers.remove(TRACKER_APP);
}
}
private static void logFbEvent(String appEventConst, Double price, Bundle parameters) {
if (facebookLogger != null) {
if (parameters == null)
facebookLogger.logEvent(appEventConst);
else {
if (price == null)
facebookLogger.logEvent(appEventConst, parameters);
else
facebookLogger.logEvent(appEventConst, price, parameters);
}
} else {
Timber.e(new RuntimeException(), "null FB facebookLogger");
}
}
private static void sendEventToAppTrackers(Map<String, String> event) {
if (mTrackers == null || mTrackers.isEmpty()) {
Timber.e(new RuntimeException(), "SendEventToAppTrackers, ERROR empty app trackers set");
} else {
Set<String> keys = mTrackers.keySet();
if (keys.contains(TRACKER_GLOBAL)) {
Timber.d("Send event to GA global: %s", event.toString());
mTrackers.get(TRACKER_GLOBAL).send(event);
}
if (keys.contains(TRACKER_APP)) {
Timber.d("Send event to GA app: %s", event.toString());
mTrackers.get(TRACKER_APP).send(event);
}
}
}
/**
* Method sets new UTM campaign.
* If analytics trackers exist, method sends events with UTM.
* If analytics trackers doesn't exist, event with UTM will be send when they are created.
*
* @param campaignUriString UTM string.
*/
public static synchronized void setCampaignUriString(String campaignUriString) {
Timber.d("Set campaign uri: %s", campaignUriString);
campaignUri = campaignUriString;
if (mTrackers != null && !mTrackers.isEmpty()) {
sendCampaignInfo();
}
}
private static synchronized void sendCampaignInfo() {
if (campaignUri != null && !campaignUri.isEmpty()) {
try {
Timber.d("Sending campaign uri.");
if (mTrackers.isEmpty())
Timber.e("Empty app trackers set");
else {
Set<String> keys = mTrackers.keySet();
for (String key : keys) {
Tracker t = mTrackers.get(key);
t.setScreenName("OpenShop");
Map<String, String> hit = new HitBuilders.ScreenViewBuilder()
.setCampaignParamsFromUrl(campaignUri)
.build();
Timber.e("Send campaign: %s", hit.toString());
// Campaign data sent with this hit.
t.send(hit);
}
}
} catch (Exception e) {
e.printStackTrace();
Timber.e(e, "Send campaign info exception.");
}
} else {
Timber.e("Campaign uri is null");
}
}
/////////////////////////////////////////////////////////////////////////////////////
////////////// Custom logging methods. //////////////////////////////////////////////
/**
* Method sends product view event to defined Analytics.
*
* @param remoteId remote id of the viewed product.
* @param name name of the viewed product.
*/
public static void logProductView(long remoteId, String name) {
// FB event log
Bundle parameters = new Bundle();
parameters.putString(AppEventsConstants.EVENT_PARAM_CONTENT_TYPE, PRODUCT);
parameters.putLong(AppEventsConstants.EVENT_PARAM_CONTENT_ID, remoteId);
parameters.putString(AppEventsConstants.EVENT_PARAM_DESCRIPTION, name);
logFbEvent(AppEventsConstants.EVENT_NAME_VIEWED_CONTENT, null, parameters);
// GA event log
Map<String, String> event = new HitBuilders.EventBuilder()
.setCategory(PRODUCT)
.setAction("view")
.setLabel("product with id: " + remoteId + ", name: " + name)
.build();
sendEventToAppTrackers(event);
}
/**
* Method sends "product add to cart" event to defined Analytics.
*
* @param remoteId remote id of the viewed product.
* @param name name of the viewed product.
* @param discountPrice product price.
*/
public static void logAddProductToCart(long remoteId, String name, double discountPrice) {
// FB facebookLogger
Bundle parameters = new Bundle();
parameters.putString(AppEventsConstants.EVENT_PARAM_CONTENT_TYPE, PRODUCT);
parameters.putLong(AppEventsConstants.EVENT_PARAM_CONTENT_ID, remoteId);
parameters.putString(AppEventsConstants.EVENT_PARAM_DESCRIPTION, name);
logFbEvent(AppEventsConstants.EVENT_NAME_ADDED_TO_CART, discountPrice, parameters);
// Ga
Map<String, String> event = new HitBuilders.EventBuilder()
.setCategory("ADDED_TO_CART")
.setAction("ADDED_TO_CART")
.setLabel("ADDED TO CART" + " product id: " + remoteId + " product name: " + name + " price: " + discountPrice)
.build();
sendEventToAppTrackers(event);
}
/**
* Method sends "user changed shop" event to defined Analytics.
*
* @param actualNonNullShop active shop before change.
* @param newShopSelected active shop after change.
*/
public static void logShopChange(Shop actualNonNullShop, Shop newShopSelected) {
if (actualNonNullShop != null && newShopSelected != null) {
String description = "From (id=" + actualNonNullShop.getId() + ",name=" + actualNonNullShop.getName() +
") to (id=" + newShopSelected.getId() + ",name=" + newShopSelected.getId() + ")";
// FB facebookLogger
Bundle parameters = new Bundle();
parameters.putString(AppEventsConstants.EVENT_PARAM_DESCRIPTION, description);
logFbEvent(AppEventsConstants.EVENT_NAME_UNLOCKED_ACHIEVEMENT, null, parameters);
// Ga
Map<String, String> event = new HitBuilders.EventBuilder()
.setCategory("CHANGE_SHOP")
.setAction("CHANGE_SHOP")
.setLabel(description)
.build();
sendEventToAppTrackers(event);
} else {
Timber.e(new RuntimeException(), "Try log shop change with null parameters");
}
}
/**
* Method sends "app opened by notification" event to Google Analytics.
*
* @param target specific notification data.
*/
public static void logOpenedByNotification(String target) {
// Ga
Map<String, String> event = new HitBuilders.EventBuilder()
.setAction("OPENED_BY_NOTIFICATION")
.setLabel("OPENED_BY_NOTIFICATION with link:" + target + ".")
.build();
sendEventToAppTrackers(event);
}
/**
* Method sends "order created" event to Google Analytics.
*
* @param orderCart ordered cart content.
* @param orderRemoteId remote order id.
* @param orderTotalPrice total order price.
* @param selectedShipping selected shipping to log its price.
*/
public static void logOrderCreatedEvent(Cart orderCart, String orderRemoteId, Double orderTotalPrice, Shipping selectedShipping) {
//GA
Map<String, String> eventPostOrder = new HitBuilders.EventBuilder()
.setCategory(POST_ORDER)
.setAction(POST_ORDER)
.setLabel(POST_ORDER)
.build();
sendEventToAppTrackers(eventPostOrder);
// Send GA whole cart
Map<String, String> event = new HitBuilders.TransactionBuilder()
.setTransactionId(orderRemoteId)
.setAffiliation(SettingsMy.getActualNonNullShop(null).getName())
.setRevenue(orderTotalPrice)
.setTax(0.0)
.setShipping(selectedShipping.getPrice())
.setCurrencyCode(orderCart.getCurrency())
.build();
sendEventToAppTrackers(event);
// Fb event whole cart
Bundle parametersCheckout = new Bundle();
parametersCheckout.putString(AppEventsConstants.EVENT_PARAM_CONTENT_TYPE, "cart");
parametersCheckout.putString(AppEventsConstants.EVENT_PARAM_CONTENT_ID, orderRemoteId);
parametersCheckout.putInt(AppEventsConstants.EVENT_PARAM_NUM_ITEMS, orderCart.getItems().size()); // Unique products/events
parametersCheckout.putString(AppEventsConstants.EVENT_PARAM_CURRENCY, orderCart.getCurrency());
logFbEvent(AppEventsConstants.EVENT_NAME_INITIATED_CHECKOUT, orderTotalPrice, parametersCheckout);
// Fb event shipping
Bundle parametersShip = new Bundle();
parametersShip.putString(AppEventsConstants.EVENT_PARAM_CONTENT_TYPE, "shipping");
parametersShip.putString(AppEventsConstants.EVENT_PARAM_CONTENT_ID, orderRemoteId);
parametersShip.putString(AppEventsConstants.EVENT_PARAM_CURRENCY, orderCart.getCurrency());
logFbEvent(AppEventsConstants.EVENT_NAME_PURCHASED, (double) selectedShipping.getPrice(), parametersShip);
// Send single products in cart to GA and FB
for (int i = 0; i < orderCart.getItems().size(); i++) {
CartProductItem item = orderCart.getItems().get(i);
Double price = item.getVariant().getPrice();
if (item.getVariant().getDiscountPrice() > 0) {
price = item.getVariant().getDiscountPrice();
}
Map<String, String> eventSingle = new HitBuilders.ItemBuilder()
.setTransactionId(orderRemoteId)
.setName(item.getVariant().getName())
.setSku("Product id: " + item.getVariant().getRemoteId())
.setCategory("Category id: " + item.getVariant().getCategory())
.setPrice(price)
.setQuantity(item.getQuantity())
.setCurrencyCode(orderCart.getCurrency())
.build();
sendEventToAppTrackers(eventSingle);
// Fb events purchased
Bundle parameters = new Bundle();
parameters.putString(AppEventsConstants.EVENT_PARAM_CONTENT_TYPE, PRODUCT);
parameters.putLong(AppEventsConstants.EVENT_PARAM_CONTENT_ID, item.getVariant().getRemoteId());
parameters.putInt(AppEventsConstants.EVENT_PARAM_NUM_ITEMS, item.getQuantity());
parameters.putString(AppEventsConstants.EVENT_PARAM_CURRENCY, orderCart.getCurrency());
logFbEvent(AppEventsConstants.EVENT_NAME_PURCHASED, price * item.getQuantity(), parameters);
}
}
/**
* Method sends "category view" event to Google Analytics.
*
* @param categoryId id category for logging.
* @param categoryName category name for logging.
* @param isSearch determine if normal category or search category.
*/
public static void logCategoryView(long categoryId, String categoryName, boolean isSearch) {
Bundle parameters = new Bundle();
parameters.putString(AppEventsConstants.EVENT_PARAM_CONTENT_TYPE, "category");
if (categoryId == 0) {
Timber.e("Is categoryId = 0.");
} else {
parameters.putLong(AppEventsConstants.EVENT_PARAM_CONTENT_ID, categoryId);
parameters.putString(AppEventsConstants.EVENT_PARAM_DESCRIPTION, categoryName);
logFbEvent(AppEventsConstants.EVENT_NAME_VIEWED_CONTENT, null, parameters);
Map<String, String> event = new HitBuilders.EventBuilder()
.setCategory("VIEW_CATEGORY")
.setAction(isSearch ? "SEARCH" : "VIEW_CATEGORY")
.setLabel(isSearch ? "Search: " + categoryName : "CategoryId: " + categoryId + ". CategoryName: " + categoryName)
.build();
sendEventToAppTrackers(event);
}
}
////////////// end of custom logging methods. ///////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
}