Skip to content

Commit

Permalink
Add Application Open and Application Backgrounded Events (#620)
Browse files Browse the repository at this point in the history
* LIB-1156: Added support for tracking event when app opened from background

* Ran formatter.

* Added Backgrounded events

* Run formatter.

* Updated lifecycle methods and added integration tests.

* Separate out Lifecycle Callbacks into separate class

* Run formatter.

* Made analytics lifecycle class package protected.
  • Loading branch information
carloskelly13 authored and Fathy Boundjadj committed Jul 23, 2019
1 parent 93c7d5b commit 1cfb025
Show file tree
Hide file tree
Showing 3 changed files with 411 additions and 102 deletions.
71 changes: 8 additions & 63 deletions analytics/src/main/java/com/segment/analytics/Analytics.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
Expand Down Expand Up @@ -75,7 +74,6 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* The entry point into the Segment for Android SDK.
Expand Down Expand Up @@ -285,61 +283,15 @@ public void run() {
logger.debug("Created analytics client for project with tag:%s.", tag);

activityLifecycleCallback =
new Application.ActivityLifecycleCallbacks() {
final AtomicBoolean trackedApplicationLifecycleEvents = new AtomicBoolean(false);
new AnalyticsActivityLifecycleCallbacks.Builder()
.analytics(this)
.analyticsExecutor(analyticsExecutor)
.shouldTrackApplicationLifecycleEvents(shouldTrackApplicationLifecycleEvents)
.trackAttributionInformation(trackAttributionInformation)
.shouldRecordScreenViews(shouldRecordScreenViews)
.packageInfo(getPackageInfo(application))
.build();

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
if (!trackedApplicationLifecycleEvents.getAndSet(true)
&& shouldTrackApplicationLifecycleEvents) {
trackApplicationLifecycleEvents();

if (trackAttributionInformation) {
analyticsExecutor.submit(
new Runnable() {
@Override
public void run() {
trackAttributionInformation();
}
});
}
}
runOnMainThread(IntegrationOperation.onActivityCreated(activity, savedInstanceState));
}

@Override
public void onActivityStarted(Activity activity) {
if (shouldRecordScreenViews) {
recordScreenViews(activity);
}
runOnMainThread(IntegrationOperation.onActivityStarted(activity));
}

@Override
public void onActivityResumed(Activity activity) {
runOnMainThread(IntegrationOperation.onActivityResumed(activity));
}

@Override
public void onActivityPaused(Activity activity) {
runOnMainThread(IntegrationOperation.onActivityPaused(activity));
}

@Override
public void onActivityStopped(Activity activity) {
runOnMainThread(IntegrationOperation.onActivityStopped(activity));
}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
runOnMainThread(IntegrationOperation.onActivitySaveInstanceState(activity, outState));
}

@Override
public void onActivityDestroyed(Activity activity) {
runOnMainThread(IntegrationOperation.onActivityDestroyed(activity));
}
};
application.registerActivityLifecycleCallbacks(activityLifecycleCallback);
}

Expand Down Expand Up @@ -405,13 +357,6 @@ void trackApplicationLifecycleEvents() {
.putValue("previous_" + BUILD_KEY, previousBuild));
}

// Track Application Opened.
track(
"Application Opened",
new Properties() //
.putValue("version", currentVersion) //
.putValue("build", currentBuild));

// Update the recorded version.
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(VERSION_KEY, currentVersion);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2014 Segment.io, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.segment.analytics;

import android.app.Activity;
import android.app.Application;
import android.content.pm.PackageInfo;
import android.os.Bundle;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

class AnalyticsActivityLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
private Analytics analytics;
private ExecutorService analyticsExecutor;
private Boolean shouldTrackApplicationLifecycleEvents;
private Boolean trackAttributionInformation;
private Boolean shouldRecordScreenViews;
private PackageInfo packageInfo;

private AtomicBoolean trackedApplicationLifecycleEvents;
private AtomicInteger numberOfActivities;
private AtomicBoolean isChangingActivityConfigurations;
private AtomicBoolean firstLaunch;

private AnalyticsActivityLifecycleCallbacks(
Analytics analytics,
ExecutorService analyticsExecutor,
Boolean shouldTrackApplicationLifecycleEvents,
Boolean trackAttributionInformation,
Boolean shouldRecordScreenViews,
PackageInfo packageInfo) {
this.trackedApplicationLifecycleEvents = new AtomicBoolean(false);
this.numberOfActivities = new AtomicInteger(1);
this.isChangingActivityConfigurations = new AtomicBoolean(false);
this.firstLaunch = new AtomicBoolean(false);
this.analytics = analytics;
this.analyticsExecutor = analyticsExecutor;
this.shouldTrackApplicationLifecycleEvents = shouldTrackApplicationLifecycleEvents;
this.trackAttributionInformation = trackAttributionInformation;
this.shouldRecordScreenViews = shouldRecordScreenViews;
this.packageInfo = packageInfo;
}

@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
analytics.runOnMainThread(IntegrationOperation.onActivityCreated(activity, bundle));

if (!trackedApplicationLifecycleEvents.getAndSet(true)
&& shouldTrackApplicationLifecycleEvents) {
numberOfActivities.set(0);
firstLaunch.set(true);
analytics.trackApplicationLifecycleEvents();

if (trackAttributionInformation) {
analyticsExecutor.submit(
new Runnable() {
@Override
public void run() {
analytics.trackAttributionInformation();
}
});
}
}
}

@Override
public void onActivityStarted(Activity activity) {
if (shouldRecordScreenViews) {
analytics.recordScreenViews(activity);
}
analytics.runOnMainThread(IntegrationOperation.onActivityStarted(activity));
}

@Override
public void onActivityResumed(Activity activity) {
analytics.runOnMainThread(IntegrationOperation.onActivityResumed(activity));

if (shouldTrackApplicationLifecycleEvents
&& numberOfActivities.incrementAndGet() == 1
&& !isChangingActivityConfigurations.get()) {

Properties properties = new Properties();
if (firstLaunch.get()) {
properties
.putValue("version", packageInfo.versionName)
.putValue("build", packageInfo.versionCode);
}
properties.putValue("from_background", !firstLaunch.getAndSet(false));
analytics.track("Application Opened", properties);
}
}

@Override
public void onActivityPaused(Activity activity) {
analytics.runOnMainThread(IntegrationOperation.onActivityPaused(activity));
}

@Override
public void onActivityStopped(Activity activity) {
analytics.runOnMainThread(IntegrationOperation.onActivityStopped(activity));

isChangingActivityConfigurations.set(activity.isChangingConfigurations());
if (shouldTrackApplicationLifecycleEvents
&& numberOfActivities.decrementAndGet() == 0
&& !isChangingActivityConfigurations.get()) {
analytics.track("Application Backgrounded");
}
}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
analytics.runOnMainThread(IntegrationOperation.onActivitySaveInstanceState(activity, bundle));
}

@Override
public void onActivityDestroyed(Activity activity) {
analytics.runOnMainThread(IntegrationOperation.onActivityDestroyed(activity));
}

public static class Builder {
private Analytics analytics;
private ExecutorService analyticsExecutor;
private Boolean shouldTrackApplicationLifecycleEvents;
private Boolean trackAttributionInformation;
private Boolean shouldRecordScreenViews;
private PackageInfo packageInfo;

public Builder() {}

public Builder analytics(Analytics analytics) {
this.analytics = analytics;
return this;
}

Builder analyticsExecutor(ExecutorService analyticsExecutor) {
this.analyticsExecutor = analyticsExecutor;
return this;
}

Builder shouldTrackApplicationLifecycleEvents(Boolean shouldTrackApplicationLifecycleEvents) {
this.shouldTrackApplicationLifecycleEvents = shouldTrackApplicationLifecycleEvents;
return this;
}

Builder trackAttributionInformation(Boolean trackAttributionInformation) {
this.trackAttributionInformation = trackAttributionInformation;
return this;
}

Builder shouldRecordScreenViews(Boolean shouldRecordScreenViews) {
this.shouldRecordScreenViews = shouldRecordScreenViews;
return this;
}

Builder packageInfo(PackageInfo packageInfo) {
this.packageInfo = packageInfo;
return this;
}

public AnalyticsActivityLifecycleCallbacks build() {
return new AnalyticsActivityLifecycleCallbacks(
analytics,
analyticsExecutor,
shouldTrackApplicationLifecycleEvents,
trackAttributionInformation,
shouldRecordScreenViews,
packageInfo);
}
}
}
Loading

0 comments on commit 1cfb025

Please sign in to comment.