diff --git a/README.md b/README.md
index 8bee511..7e70552 100644
--- a/README.md
+++ b/README.md
@@ -1,26 +1,26 @@
-analytics-android-integration-quantcast
+analytics-android-integration-firebase
=======================================
-[](https://maven-badges.herokuapp.com/maven-central/com.segment.analytics.android.integrations/quantcast)
-[](http://javadoc-badge.appspot.com/com.segment.analytics.android.integrations/quantcast)
+[![Maven Central]()
+[![Javadocs]()
-Quantcast integration for [analytics-android](https://github.com/segmentio/analytics-android).
+Firebase Analytics integration for [analytics-android](https://github.com/segmentio/analytics-android).
## Installation
-To install the Segment-Quantcast integration, simply add this line to your gradle file:
+To install the Segment-Firebase integration, simply add this line to your gradle file:
```
-compile 'com.segment.analytics.android.integrations:quantcast:+'
+compile 'com.segment.analytics.android.integrations:firebase-analytics:+'
```
## Usage
-After adding the dependency, you must register the integration with our SDK. To do this, import the Quantcast integration:
+After adding the dependency, you must register the integration with our SDK. To do this, import the Firebase integration:
```
-import com.segment.analytics.android.integrations.quantcast.QuantcastIntegration;
+import com.segment.analytics.android.integrations.firebase-analytics.FirebaseIntegration;
```
@@ -28,11 +28,11 @@ And add the following line:
```
analytics = new Analytics.Builder(this, "write_key")
- .use(QuantcastIntegration.FACTORY)
+ .use(FirebaseIntegration.FACTORY)
.build();
```
-Please see [our documentation](https://segment.com/docs/integrations/quantcast/) for more information.
+Please see [our documentation](https://segment.com/docs/integrations/firebase-analytics/) for more information.
## License
diff --git a/build.gradle b/build.gradle
index 2fcab7f..64d127f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,26 +1,25 @@
buildscript {
repositories {
- mavenCentral()
+ jcenter()
}
dependencies {
- classpath 'com.android.tools.build:gradle:1.3.1'
- classpath 'com.f2prateek.checkstyle:checkstyle:1.0.1'
+ classpath 'com.android.tools.build:gradle:2.3.3'
+ classpath 'com.f2prateek.javafmt:javafmt:0.1.6'
}
}
apply plugin: 'com.android.library'
-apply plugin: 'checkstyle'
-apply plugin: 'com.f2prateek.checkstyle'
+apply plugin: 'com.f2prateek.javafmt'
android {
- compileSdkVersion 23
- buildToolsVersion '23.0.1'
+ compileSdkVersion 26
+ buildToolsVersion '25.0.3'
defaultConfig {
minSdkVersion 14
- targetSdkVersion 23
- compileSdkVersion 23
+ targetSdkVersion 26
+ compileSdkVersion 26
}
compileOptions {
@@ -28,43 +27,39 @@ android {
targetCompatibility JavaVersion.VERSION_1_7
}
- lintOptions {
- // Okio https://cloudup.com/cp7bi10o2C3g
- disable 'InvalidPackage'
+ testOptions {
+ unitTests.returnDefaultValues = true
}
}
-checkstyle {
- configFile rootProject.file('gradle/checkstyle.xml')
-}
-
dependencies {
repositories {
- mavenCentral()
+ jcenter()
+ maven {
+ url "https://maven.google.com/"
+ }
}
- provided 'com.segment.analytics.android:analytics:4.0.9'
-
compile 'com.google.firebase:firebase-core:11.2.0'
+ compile 'com.segment.analytics.android:analytics:4.2.6'
+
+ testCompile 'com.segment.analytics.android:analytics-tests:4.2.6'
+
testCompile 'junit:junit:4.12'
- testCompile('org.robolectric:robolectric:3.0') {
- exclude group: 'commons-logging', module: 'commons-logging'
- exclude group: 'org.apache.httpcomponents', module: 'httpclient'
- }
- testCompile 'com.segment.analytics.android:analytics-tests:4.0.9'
+ testCompile 'org.robolectric:robolectric:3.4.2'
testCompile 'org.assertj:assertj-core:1.7.1'
testCompile 'org.mockito:mockito-core:1.10.19'
- testCompile 'org.powermock:powermock:1.6.2'
- testCompile 'org.powermock:powermock-module-junit4:1.6.2'
- testCompile 'org.powermock:powermock-module-junit4-rule:1.6.2'
- testCompile 'org.powermock:powermock-api-mockito:1.6.2'
- testCompile 'org.powermock:powermock-classloading-xstream:1.6.2'
+ testCompile 'org.powermock:powermock:1.6.6'
+ testCompile 'org.powermock:powermock-module-junit4:1.6.6'
+ testCompile 'org.powermock:powermock-module-junit4-rule:1.6.6'
+ testCompile 'org.powermock:powermock-api-mockito:1.6.6'
+ testCompile 'org.powermock:powermock-classloading-xstream:1.6.6'
}
+apply plugin: 'com.f2prateek.javafmt'
apply from: rootProject.file('gradle/gradle-mvn-push.gradle')
-apply plugin: 'com.google.gms.google-services'
diff --git a/gradle.properties b/gradle.properties
index 17468b0..99c9d8c 100755
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,18 +1,18 @@
GROUP=com.segment.analytics.android.integrations
-VERSION_CODE=
-VERSION_NAME=
+VERSION_CODE=001
+VERSION_NAME=0.0.1-SNAPSHOT
-POM_ARTIFACT_ID=
-POM_PACKAGING=jar
+POM_ARTIFACT_ID=firebase
+POM_PACKAGING=aar
-POM_NAME=
-POM_DESCRIPTION=
+POM_NAME=Firebase Analytics Integration
+POM_DESCRIPTION=Firebase Analytics Integration for Segment Android Analytics
-POM_URL=
-POM_SCM_URL=
-POM_SCM_CONNECTION=
-POM_SCM_DEV_CONNECTION=
+POM_URL=http://github.com/segment-integrations/analytics-android-integration-firebase
+POM_SCM_URL=http://github.com/segment-integrations/analytics-android-integration-firebase
+POM_SCM_CONNECTION=scm:git:git://github.com/segment-integrations/analytics-android-integration-firebase.git
+POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/segment-integrations/analytics-android-integration-firebase.git
POM_LICENCE_NAME=The MIT License (MIT)
POM_LICENCE_URL=http://opensource.org/licenses/MIT
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 1d242e1..ef49179 100755
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Fri Dec 26 13:17:50 MST 2014
+#Wed Aug 23 10:10:41 PDT 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.7-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 5f84218..9c08ff9 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -1 +1,10 @@
-
+
+
+
+
+
+
+
+
+
diff --git a/src/main/java/com/segment/analytics/android/integrations/firebase/FirebaseIntegration.java b/src/main/java/com/segment/analytics/android/integrations/firebase/FirebaseIntegration.java
index caf2054..935d05c 100644
--- a/src/main/java/com/segment/analytics/android/integrations/firebase/FirebaseIntegration.java
+++ b/src/main/java/com/segment/analytics/android/integrations/firebase/FirebaseIntegration.java
@@ -3,23 +3,32 @@
import android.Manifest;
import android.app.Activity;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
-import com.google.firebase.analytics.FirebaseAnalytics; // FirebaseAnalytics class
+import com.google.firebase.analytics.FirebaseAnalytics;
+import com.google.firebase.analytics.FirebaseAnalytics.Event;
+import com.google.firebase.analytics.FirebaseAnalytics.Param;
import com.segment.analytics.Analytics;
+import com.segment.analytics.Properties;
import com.segment.analytics.ValueMap;
import com.segment.analytics.integrations.IdentifyPayload;
import com.segment.analytics.integrations.Integration;
import com.segment.analytics.integrations.Logger;
-import com.segment.analytics.integrations.ScreenPayload;
import com.segment.analytics.integrations.TrackPayload;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Date;
+
import static com.segment.analytics.internal.Utils.hasPermission;
import static com.segment.analytics.internal.Utils.isNullOrEmpty;
-import static com.segment.analytics.Analytics.LogLevel.VERBOSE;
+import static com.segment.analytics.internal.Utils.toISO8601Date;
/**
- * Google Analytics for Firebase is a free app measurement solution that provides insight on
- * app usage and user engagement.
+ * Google Analytics for Firebase is a free app measurement solution that provides insight on app
+ * usage and user engagement.
*
* @see Google Analytics for Firebase
* @see Google Analytics for Firebase Integration
@@ -27,64 +36,208 @@
*/
public class FirebaseIntegration extends Integration {
- public static FirebaseAnalytics mFirebaseAnalytics;
+ public static final Factory FACTORY =
+ new Factory() {
+ @Override
+ public Integration> create(ValueMap settings, Analytics analytics) {
+ Logger logger = analytics.logger(FIREBASE_ANALYTICS_KEY);
+ if (!hasPermission(
+ analytics.getApplication(), Manifest.permission.ACCESS_NETWORK_STATE)) {
+ logger.debug("ACCESS_NETWORK_STATE is required for Firebase Analytics.");
+ return null;
+ }
+ if (!hasPermission(analytics.getApplication(), Manifest.permission.WAKE_LOCK)) {
+ logger.debug("WAKE_LOCK is required for Firebase Analytics.");
+ return null;
+ }
+
+ Context context = analytics.getApplication();
+
+ return new FirebaseIntegration(context, logger);
+ }
+
+ @Override
+ public String key() {
+ return FIREBASE_ANALYTICS_KEY;
+ }
+ };
+
+ private static final String FIREBASE_ANALYTICS_KEY = "Firebase";
+ private static final Map eventMapper = createEventMap();
+
+ private static Map createEventMap() {
+ Map eventMapper = new HashMap<>();
+ eventMapper.put("Product Added", Event.ADD_TO_CART);
+ eventMapper.put("Checkout Started", Event.BEGIN_CHECKOUT);
+ eventMapper.put("Order Completed", Event.ECOMMERCE_PURCHASE);
+ eventMapper.put("Order Refunded", Event.PURCHASE_REFUND);
+ eventMapper.put("Product Viewed", Event.VIEW_ITEM);
+ eventMapper.put("Product List Viewed", Event.VIEW_ITEM_LIST);
+ eventMapper.put("Payment Info Entered", Event.ADD_PAYMENT_INFO);
+ eventMapper.put("Promotion Viewed", Event.PRESENT_OFFER);
+ eventMapper.put("Product Added to Wishlist", Event.ADD_TO_WISHLIST);
+ eventMapper.put("Product Shared", Event.SHARE);
+ eventMapper.put("Product Clicked", Event.SELECT_CONTENT);
+ eventMapper.put("Product Searched", Event.SEARCH);
+ eventMapper.put("Promotion Viewed", Event.PRESENT_OFFER);
+ return eventMapper;
+ }
- public static final Factory FACTORY = new Factory() {
- @Override public Integration> create(ValueMap settings, Analytics analytics) {
- Logger logger = analytics.logger(FIREBASE_ANALYTICS_KEY);
- if (!hasPermission(analytics.getApplication(), Manifest.permission.ACCESS_NETWORK_STATE)) {
- logger.debug("ACCESS_NETWORK_STATE is required for Firebase Analytics.");
- return null;
- }
- if (!hasPermission(analytics.getApplication(), Manifest.permission.INTERNET)) {
- logger.debug("INTERNET is required for Firebase Analytics.");
- return null;
- }
+ private static final Map propertyMapper = createPropertyMap();
+
+ private static Map createPropertyMap() {
+ Map propertyMapper = new HashMap<>();
+ propertyMapper.put("category", Param.ITEM_CATEGORY);
+ propertyMapper.put("product_id", Param.ITEM_ID);
+ propertyMapper.put("name", Param.ITEM_NAME);
+ propertyMapper.put("price", Param.PRICE);
+ propertyMapper.put("quantity", Param.QUANTITY);
+ propertyMapper.put("query", Param.SEARCH_TERM);
+ propertyMapper.put("shipping", Param.SHIPPING);
+ propertyMapper.put("tax", Param.TAX);
+ propertyMapper.put("total", Param.VALUE);
+ propertyMapper.put("revenue", Param.VALUE);
+ propertyMapper.put("order_id", Param.TRANSACTION_ID);
+ propertyMapper.put("currency", Param.CURRENCY);
+ return propertyMapper;
+ }
- Context context = analytics.getApplication();
+ private final Logger logger;
+ private final FirebaseAnalytics firebaseAnalytics;
- return new FirebaseIntegration(context);
- }
+ public FirebaseIntegration(Context context, Logger logger) {
+ this.firebaseAnalytics = FirebaseAnalytics.getInstance(context);
+ this.logger = logger;
+ }
- @Override public String key() {
- return FIREBASE_ANALYTICS_KEY;
+ @Override
+ public void onActivityResumed(Activity activity) {
+ super.onActivityResumed(activity);
+
+ PackageManager packageManager = activity.getPackageManager();
+ try {
+ ActivityInfo info =
+ packageManager.getActivityInfo(activity.getComponentName(), PackageManager.GET_META_DATA);
+ String activityLabel = info.loadLabel(packageManager).toString();
+ firebaseAnalytics.setCurrentScreen(activity, activityLabel, null);
+ logger.verbose("firebaseAnalytics.setCurrentScreen(activity, %s, null);", activityLabel);
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new AssertionError("Activity Not Found: " + e.toString());
}
- };
-
- private static final String FIREBASE_ANALYTICS_KEY = "Firebase Analytics";
-
- public FirebaseIntegration(Context context) {
- mFirebaseAnalytics = FirebaseAnalytics.getInstance(context);
}
- @Override public void onActivityStarted(Activity activity) {
- super.onActivityStarted(activity);
+ @Override
+ public void identify(IdentifyPayload identify) {
+ super.identify(identify);
+ if (!isNullOrEmpty(identify.userId())) {
+ firebaseAnalytics.setUserId(identify.userId());
+ }
+ Map traits = identify.traits();
+ for (Map.Entry entry : traits.entrySet()) {
+ String trait = entry.getKey();
+ trait = trait.trim().replaceAll(" ", "_");
+ if (trait.length() > 40) {
+ trait = trimKey(trait);
+ }
+ String formattedValue;
+ if (entry.getValue() instanceof Date) {
+ Date value = (Date) entry.getValue();
+ formattedValue = formatDate(value);
+ } else {
+ formattedValue = String.valueOf(entry.getValue());
+ }
+ firebaseAnalytics.setUserProperty(trait, formattedValue);
+ logger.verbose("firebaseAnalytics.setUserProperty(%s, %s);", trait, formattedValue);
+ }
}
- @Override public void onActivityStopped(Activity activity) {
- super.onActivityStopped(activity);
+ @Override
+ public void track(TrackPayload track) {
+ super.track(track);
+ String event = track.event();
+ String eventName = mapEvent(event);
+ Properties properties = track.properties();
+ Bundle formattedProperties = formatProperties(properties);
+ firebaseAnalytics.logEvent(eventName, formattedProperties);
+ logger.verbose("firebaseAnalytics.logEvent(%s, %s);", eventName, formattedProperties);
}
- @Override public void identify(IdentifyPayload identify) {
- super.identify(identify);
-
- if (!isNullOrEmpty(identify.userId())) {
- mFirebaseAnalytics.setUserId(identify.userId());
+ private String mapEvent(String event) {
+ String eventName = event;
+ if (eventMapper.containsKey(eventName)) {
+ eventName = eventMapper.get(eventName);
}
+ eventName = eventName.trim().replaceAll(" ", "_");
+ if (eventName.length() > 40) {
+ eventName = trimKey(eventName);
+ }
+ return eventName;
+ }
- // mFirebaseAnalytics.setUserProperty(key, value);
-
+ private Bundle formatProperties(Properties properties) {
+ Bundle bundle = new Bundle();
+ if ((properties.revenue() != 0 || properties.total() != 0)
+ && isNullOrEmpty(properties.currency())) {
+ bundle.putString(Param.CURRENCY, "USD");
+ }
+ for (Map.Entry entry : properties.entrySet()) {
+ String property = entry.getKey();
+ property = mapProperty(property);
+ if (entry.getValue() instanceof Integer) {
+ int value = (int) entry.getValue();
+ bundle.putInt(property, value);
+ logger.verbose("bundle.putInt(%s, %s);", property, value);
+ continue;
+ }
+ if (entry.getValue() instanceof Double) {
+ double value = (double) entry.getValue();
+ bundle.putDouble(property, value);
+ logger.verbose("bundle.putDouble(%s, %s);", property, value);
+ continue;
+ }
+ if (entry.getValue() instanceof Long) {
+ long value = (long) entry.getValue();
+ bundle.putLong(property, value);
+ logger.verbose("bundle.putLong(%s, %s);", property, value);
+ continue;
+ }
+ if (entry.getValue() instanceof String) {
+ String value = String.valueOf(entry.getValue());
+ bundle.putString(property, value);
+ logger.verbose("bundle.putString(%s, %s);", property, value);
+ continue;
+ }
+ if (entry.getValue() instanceof Date) {
+ Date value = (Date) entry.getValue();
+ String formattedDate = formatDate(value);
+ bundle.putString(property, formattedDate);
+ logger.verbose("bundle.putString(%s, %s);", property, formattedDate);
+ continue;
+ }
+ }
+ return bundle;
}
- @Override public void screen(ScreenPayload screen) {
- super.screen(screen);
-// mFirebaseAnalytics.setCurrentScreen(this, screen.name(), null /* class override */);
+ private String mapProperty(String property) {
+ if (propertyMapper.containsKey(property)) {
+ property = propertyMapper.get(property);
+ }
+ property = property.trim().replaceAll(" ", "_");
+ if (property.length() > 40) {
+ property = trimKey(property);
+ }
+ return property;
}
- @Override public void track(TrackPayload track) {
- super.track(track);
+ private String trimKey(String string) {
+ return string.substring(0, Math.min(string.length(), 40));
+ }
+ private String formatDate(Date date) {
+ String stringifiedValue = toISO8601Date(date);
+ String truncatedValue = stringifiedValue.substring(0, 10);
+ return truncatedValue;
}
}
diff --git a/src/test/java/com/segment/analytics/android/integration/firebase/FirebaseTest.java b/src/test/java/com/segment/analytics/android/integration/firebase/FirebaseTest.java
new file mode 100644
index 0000000..ed09430
--- /dev/null
+++ b/src/test/java/com/segment/analytics/android/integration/firebase/FirebaseTest.java
@@ -0,0 +1,57 @@
+package com.segment.analytics.android.integration.firebase;
+
+import static com.segment.analytics.Analytics.LogLevel.VERBOSE;
+import static com.segment.analytics.Utils.createTraits;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+
+import android.content.Context;
+import com.google.firebase.analytics.FirebaseAnalytics;
+import com.segment.analytics.Traits;
+import com.segment.analytics.android.integrations.firebase.FirebaseIntegration;
+import com.segment.analytics.core.tests.BuildConfig;
+import com.segment.analytics.integrations.Logger;
+import com.segment.analytics.test.IdentifyPayloadBuilder;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.core.classloader.annotations.PowerMockIgnore;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+import org.powermock.modules.junit4.rule.PowerMockRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(constants = BuildConfig.class)
+@PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" })
+@PrepareForTest(FirebaseAnalytics.class)
+public class FirebaseTest {
+
+ @Rule public PowerMockRule rule = new PowerMockRule();
+ private FirebaseAnalytics firebase;
+ private FirebaseIntegration integration;
+
+ @Before
+ public void setUp() {
+ firebase = PowerMockito.mock(FirebaseAnalytics.class);
+ Context context = PowerMockito.mock(Context.class);
+
+ PowerMockito.mockStatic(FirebaseAnalytics.class);
+ Mockito.when(FirebaseAnalytics.getInstance(context)).thenReturn(firebase);
+
+ integration = new FirebaseIntegration(context, Logger.with(VERBOSE));
+ }
+
+ @Test
+ public void identify() {
+ Traits traits = createTraits("foo");
+
+ integration.identify(new IdentifyPayloadBuilder().traits(traits).build());
+
+ verify(firebase).setUserId("foo");
+ }
+}