diff --git a/Application/app/build.gradle b/Application/app/build.gradle
index edb4d04..5e5f689 100644
--- a/Application/app/build.gradle
+++ b/Application/app/build.gradle
@@ -21,6 +21,9 @@ apply from: "../artifacts.gradle"
repositories {
// Uncomment when using CrashlyticsComponent
//maven { url 'https://maven.fabric.io/public' }
+
+// Uncomment when using TealiumAnalyticsComponent
+// maven { url "http://maven.tealiumiq.com/android/releases/" }
}
buildscript {
@@ -83,10 +86,10 @@ dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile 'junit:junit:4.12'
androidTestCompile 'org.mockito:mockito-core:1.9.5'
- androidTestCompile ('com.android.support.test:rules:0.5') {
+ androidTestCompile('com.android.support.test:rules:0.5') {
exclude group: 'com.android.support', module: 'support-annotations'
}
- androidTestCompile ('com.android.support.test:runner:0.5') {
+ androidTestCompile('com.android.support.test:runner:0.5') {
exclude group: 'com.android.support', module: 'support-annotations'
}
androidTestCompile 'com.google.dexmaker:dexmaker:1.2'
@@ -100,4 +103,6 @@ dependencies {
compile project(':PassThroughAdsComponent')
compile project(':PassThroughLoginComponent')
compile project(':LoggerAnalyticsComponent')
+// Uncomment when using TealiumAnalyticsComponent
+// compile project(':TealiumAnalyticsComponent')
}
diff --git a/Application/settings.gradle b/Application/settings.gradle
index 4912b06..6db69ed 100644
--- a/Application/settings.gradle
+++ b/Application/settings.gradle
@@ -24,7 +24,7 @@ include ':app',
':DataLoader',
':Utils',
// Uncomment when using ComScore Analytics
- //':comscore',
+// ':comscore',
/* Interfaces */
':PurchaseInterface',
':AuthInterface',
@@ -36,6 +36,8 @@ include ':app',
':AMZNMediaPlayerComponent',
':PassThroughLoginComponent',
':LoggerAnalyticsComponent'
+ // Uncomment when using Tealium Analytics
+// ':TealiumAnalyticsComponent'
/* Frameworks */
project(':TVUIComponent').projectDir = new File(rootProject.projectDir, '../TVUIComponent/lib')
@@ -63,3 +65,5 @@ project(':PassThroughLoginComponent').projectDir = new File(rootProject.projectD
project(':LoggerAnalyticsComponent').projectDir = new File(rootProject.projectDir, '../LoggerAnalyticsComponent')
// Uncomment when using ComScore Analytics
//project(':comscore').projectDir = new File(rootProject.projectDir, '../ComScoreAnalyticsComponent/libs/comscore')
+// Uncomment when using Tealium Analytics
+//project(':TealiumAnalyticsComponent').projectDir = new File(rootProject.projectDir, '../TealiumAnalyticsComponent')
diff --git a/TealiumAnalyticsComponent/build.gradle b/TealiumAnalyticsComponent/build.gradle
new file mode 100644
index 0000000..b2c0b12
--- /dev/null
+++ b/TealiumAnalyticsComponent/build.gradle
@@ -0,0 +1,60 @@
+apply plugin: 'com.android.library'
+
+repositories {
+ mavenCentral()
+
+ maven {
+ url "http://maven.tealiumiq.com/android/releases/"
+ }
+}
+
+android {
+ compileSdkVersion 28
+ buildToolsVersion "28.0.0"
+
+
+ defaultConfig {
+ minSdkVersion 21
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ compile project(':ModuleInterface')
+ compile project(':AnalyticsInterface')
+
+ testCompile('org.robolectric:robolectric:3.0') {
+ exclude group: 'commons-logging', module: 'commons-logging'
+ exclude group: 'org.apache.httpcomponents', module: 'httpclient'
+ }
+
+ compile 'com.tealium:library:5.5.1'
+ testCompile 'junit:junit:4.12'
+ testCompile 'org.mockito:mockito-core:1.10.19'
+ 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'
+
+ androidTestCompile 'junit:junit:4.12'
+ androidTestCompile 'com.android.support.test:rules:0.5'
+ androidTestCompile('com.android.support.test:runner:0.5') {
+ exclude module: 'support-annotations'
+ }
+
+}
diff --git a/TealiumAnalyticsComponent/src/main/AndroidManifest.xml b/TealiumAnalyticsComponent/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..f0cfa17
--- /dev/null
+++ b/TealiumAnalyticsComponent/src/main/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/TealiumAnalyticsComponent/src/main/java/com/amazon/analytics/tealium/TealiumAnalytics.java b/TealiumAnalyticsComponent/src/main/java/com/amazon/analytics/tealium/TealiumAnalytics.java
new file mode 100644
index 0000000..f4ca323
--- /dev/null
+++ b/TealiumAnalyticsComponent/src/main/java/com/amazon/analytics/tealium/TealiumAnalytics.java
@@ -0,0 +1,121 @@
+package com.amazon.analytics.tealium;
+
+import android.app.Activity;
+import android.app.Application;
+import android.content.Context;
+import android.util.Log;
+
+import com.amazon.analytics.AnalyticsTags;
+import com.amazon.analytics.CustomAnalyticsTags;
+import com.amazon.analytics.IAnalytics;
+import com.tealium.library.Tealium;
+
+import java.util.HashMap;
+import java.util.concurrent.TimeUnit;
+
+import static com.tealium.library.DataSources.Key.VIDEO_ID;
+import static com.tealium.library.DataSources.Key.VIDEO_LENGTH;
+import static com.tealium.library.DataSources.Key.VIDEO_MILESTONE;
+import static com.tealium.library.DataSources.Key.VIDEO_NAME;
+import static com.tealium.library.DataSources.Key.VIDEO_PLAYHEAD;
+
+public class TealiumAnalytics implements IAnalytics {
+
+ private static final String TAG = TealiumAnalytics.class.getSimpleName();
+ private Tealium mTealium;
+ private CustomAnalyticsTags mCustomAnalyticsTags = new CustomAnalyticsTags();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void configure(Context context) {
+ Application application = (Application) context.getApplicationContext();
+ Tealium.Config config = Tealium.Config.create(application, "", "", "");
+ mTealium = Tealium.createInstance("", config);
+
+ Log.d(TAG, "Tealium Analytics initialized");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void collectLifeCycleData(Activity activity, boolean active) {
+ Log.d(TAG, "Lifecycle is not supported for this platform.");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * @param data Map of Strings to Objects that represent data that is necessary for the tracked
+ */
+ @Override
+ public void trackAction(HashMap data) {
+ HashMap contextData = new HashMap<>();
+
+ // Get the action name
+ String action = String.valueOf(data.get(AnalyticsTags.ACTION_NAME));
+ contextData.put(AnalyticsTags.ACTION_NAME, action);
+
+ // Get the attributes map
+ HashMap contextDataObjectMap = (HashMap) data.get(AnalyticsTags.ATTRIBUTES);
+
+ if (action != null && contextDataObjectMap != null) {
+ for (String key : contextDataObjectMap.keySet()) {
+ String value = String.valueOf(contextDataObjectMap.get(key));
+ Long videoDuration;
+
+ switch (key) {
+ case AnalyticsTags.ATTRIBUTE_VIDEO_CURRENT_POSITION:
+ contextData.put(VIDEO_PLAYHEAD, value);
+ break;
+ case AnalyticsTags.ATTRIBUTE_VIDEO_ID:
+ contextData.put(VIDEO_ID, value);
+ break;
+ case AnalyticsTags.ATTRIBUTE_VIDEO_DURATION:
+ videoDuration = (Long) contextDataObjectMap.get(AnalyticsTags.ATTRIBUTE_VIDEO_DURATION);
+ contextData.put(VIDEO_LENGTH, getVideoDuration(videoDuration));
+ break;
+ case AnalyticsTags.ATTRIBUTE_VIDEO_SECONDS_WATCHED:
+ videoDuration = (Long) contextDataObjectMap.get(AnalyticsTags.ATTRIBUTE_VIDEO_DURATION);
+ if (videoDuration != null) {
+ contextData.put(VIDEO_MILESTONE, getMilestone(videoDuration, Long.valueOf(value)));
+ }
+ break;
+ case AnalyticsTags.ATTRIBUTE_TITLE:
+ contextData.put(VIDEO_NAME, value);
+ break;
+ default:
+ contextData.put(key, value);
+ break;
+ }
+ }
+
+ mTealium.trackEvent(mCustomAnalyticsTags.getCustomTag(action),
+ mCustomAnalyticsTags.getCustomTags(contextData));
+ Log.d(TAG, "Track action " + action + "with attributes " + contextData);
+ }
+ }
+
+ @Override
+ public void trackState(String screen) {
+ mTealium.trackView(screen, null);
+ Log.d(TAG, "Track screen: " + screen);
+ }
+
+ @Override
+ public void trackCaughtError(String errorMessage, Throwable t) {
+ mTealium.trackEvent(errorMessage, null);
+ Log.d(TAG, "Tracking caught error: " + errorMessage);
+ }
+
+ private String getVideoDuration(Long videoDuration) {
+ return String.valueOf(TimeUnit.MILLISECONDS.toSeconds(videoDuration));
+ }
+
+ private String getMilestone(Long videoDuration, Long secondsWatched) {
+ int percentage = (int) ((double) secondsWatched / videoDuration * 100);
+ return String.valueOf(percentage);
+ }
+}
diff --git a/TealiumAnalyticsComponent/src/main/java/com/amazon/analytics/tealium/TealiumAnalyticsImplCreator.java b/TealiumAnalyticsComponent/src/main/java/com/amazon/analytics/tealium/TealiumAnalyticsImplCreator.java
new file mode 100644
index 0000000..60af3b5
--- /dev/null
+++ b/TealiumAnalyticsComponent/src/main/java/com/amazon/analytics/tealium/TealiumAnalyticsImplCreator.java
@@ -0,0 +1,15 @@
+package com.amazon.analytics.tealium;
+
+import com.amazon.analytics.IAnalytics;
+import com.amazon.android.module.IImplCreator;
+
+public class TealiumAnalyticsImplCreator implements IImplCreator {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public IAnalytics createImpl() {
+ return new TealiumAnalytics();
+ }
+}
diff --git a/TealiumAnalyticsComponent/src/main/res/values/strings.xml b/TealiumAnalyticsComponent/src/main/res/values/strings.xml
new file mode 100644
index 0000000..8542005
--- /dev/null
+++ b/TealiumAnalyticsComponent/src/main/res/values/strings.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/TealiumAnalyticsComponent/src/test/java/com/amazon/analytics/tealium/TealiumAnalyticsImplCreatorTest.java b/TealiumAnalyticsComponent/src/test/java/com/amazon/analytics/tealium/TealiumAnalyticsImplCreatorTest.java
new file mode 100644
index 0000000..4b2648c
--- /dev/null
+++ b/TealiumAnalyticsComponent/src/test/java/com/amazon/analytics/tealium/TealiumAnalyticsImplCreatorTest.java
@@ -0,0 +1,33 @@
+package com.amazon.analytics.tealium;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Tests the {@link TealiumAnalyticsImplCreator} class
+ */
+@RunWith(RobolectricTestRunner.class)
+public class TealiumAnalyticsImplCreatorTest {
+
+ private TealiumAnalyticsImplCreator mTealiumAnalyticsImplCreator;
+
+ @Before
+ public void setUp() {
+ mTealiumAnalyticsImplCreator = new TealiumAnalyticsImplCreator();
+ }
+
+ @After
+ public void tearDown() {
+ mTealiumAnalyticsImplCreator = null;
+ }
+
+ @Test
+ public void createImpl() {
+ assertTrue("createImpl() should create a TealiumAnalyticsObject", mTealiumAnalyticsImplCreator.createImpl() instanceof TealiumAnalytics);
+ }
+}
diff --git a/TealiumAnalyticsComponent/src/test/java/com/amazon/analytics/tealium/TealiumAnalyticsTest.java b/TealiumAnalyticsComponent/src/test/java/com/amazon/analytics/tealium/TealiumAnalyticsTest.java
new file mode 100644
index 0000000..3285193
--- /dev/null
+++ b/TealiumAnalyticsComponent/src/test/java/com/amazon/analytics/tealium/TealiumAnalyticsTest.java
@@ -0,0 +1,122 @@
+package com.amazon.analytics.tealium;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.res.AssetManager;
+
+import com.amazon.analytics.AnalyticsTags;
+import com.tealium.library.Tealium;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+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;
+
+import java.util.HashMap;
+
+import static org.junit.Assert.assertNotNull;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.MockitoAnnotations.initMocks;
+import static org.powermock.api.mockito.PowerMockito.doReturn;
+import static org.powermock.api.mockito.PowerMockito.when;
+
+/**
+ * Test suite for the {@link TealiumAnalytics} class
+ */
+@RunWith(RobolectricTestRunner.class)
+@Config(constants = BuildConfig.class, sdk = 21, manifest = Config.NONE)
+@PowerMockIgnore({"org.mockito.*", "org.robolectric.*", "android.*", "org.json.*"})
+@PrepareForTest({com.tealium.library.Tealium.Config.class, com.tealium.library.Tealium.class})
+public class TealiumAnalyticsTest {
+
+ @Rule
+ public PowerMockRule rule = new PowerMockRule();
+
+ @Mock
+ AssetManager mAssetManager;
+
+ @Mock
+ Context mContext;
+
+ @Mock
+ Application mApplication;
+
+ @Mock
+ Tealium mTealium;
+
+ @Mock
+ com.tealium.library.Tealium.Config mConfig;
+
+ private TealiumAnalytics mTealiumAnalytics;
+
+ @Before
+ public void setUp() {
+ mTealiumAnalytics = new TealiumAnalytics();
+ initMocks(this);
+ PowerMockito.mockStatic(com.tealium.library.Tealium.Config.class);
+ PowerMockito.mockStatic(com.tealium.library.Tealium.class);
+ doReturn(mApplication).when(mContext).getApplicationContext();
+ when(mContext.getAssets()).thenReturn(mAssetManager);
+ when(Tealium.Config.create(any(Application.class), anyString(), anyString(), anyString())).thenReturn(mConfig);
+ when(Tealium.createInstance(anyString(), any(Tealium.Config.class))).thenReturn(mTealium);
+ }
+
+ @After
+ public void tearDown() {
+ mTealiumAnalytics = null;
+ }
+
+ @Test
+ public void configure() {
+ mTealiumAnalytics.configure(mContext);
+
+ assertNotNull(mTealiumAnalytics);
+ }
+
+ @Test
+ public void trackAction() {
+ mTealiumAnalytics.configure(mContext);
+ final String TEST_ACTION = "testAction";
+ HashMap dummyMap = new HashMap<>();
+ HashMap contextData = new HashMap();
+
+ dummyMap.put(AnalyticsTags.ACTION_NAME, TEST_ACTION);
+ contextData.put(AnalyticsTags.ACTION_NAME, TEST_ACTION);
+
+ contextData.put("key", "value");
+ dummyMap.put(AnalyticsTags.ATTRIBUTES, contextData);
+
+ mTealiumAnalytics.trackAction(dummyMap);
+
+ verify(mTealium, times(1)).trackEvent(TEST_ACTION, contextData);
+ }
+
+ @Test
+ public void trackState() {
+ mTealiumAnalytics.configure(mContext);
+ String screenName = "screenName";
+ mTealiumAnalytics.trackState(screenName);
+
+ verify(mTealium, times(1)).trackView(screenName, null);
+ }
+
+ @Test
+ public void trackCaughtError() {
+ mTealiumAnalytics.configure(mContext);
+ mTealiumAnalytics.trackCaughtError("error", new Exception());
+
+ verify(mTealium, times(1)).trackEvent("error", null);
+ }
+
+}
\ No newline at end of file